From 6ae68d9d5fef761427a4714e8ba44c23e701de93 Mon Sep 17 00:00:00 2001 From: Tim Sneath Date: Sat, 22 Jun 2019 17:41:35 -0700 Subject: [PATCH] Architectural cleanup --- lib/joke.dart | 15 +++++ lib/jokeserver.dart | 22 +++++++ lib/mainPage.dart | 143 ++++++++++++++++++++++---------------------- 3 files changed, 108 insertions(+), 72 deletions(-) create mode 100644 lib/joke.dart create mode 100644 lib/jokeserver.dart diff --git a/lib/joke.dart b/lib/joke.dart new file mode 100644 index 0000000..93f1c60 --- /dev/null +++ b/lib/joke.dart @@ -0,0 +1,15 @@ +class Joke { + final String id; + final String body; + final int status; + + Joke({this.id, this.body, this.status}); + + factory Joke.fromJson(Map json) { + return Joke( + id: json['id'], + body: json['joke'], + status: json['status'], + ); + } +} diff --git a/lib/jokeserver.dart b/lib/jokeserver.dart new file mode 100644 index 0000000..fcb40eb --- /dev/null +++ b/lib/jokeserver.dart @@ -0,0 +1,22 @@ +import 'dart:convert'; + +import 'package:dadjokes/joke.dart'; +import 'package:http/http.dart' as http; + +class JokeServer { + final dadJokeApi = "https://icanhazdadjoke.com/"; + final httpHeaders = const { + 'User-Agent': 'DadJokes (https://github.com/timsneath/dadjokes)', + 'Accept': 'application/json', + }; + + Future fetchJoke() async { + final response = await http.get(dadJokeApi, headers: httpHeaders); + + if (response.statusCode == 200) { + return Joke.fromJson(json.decode(response.body)); + } else { + throw Exception("Failed to load joke"); + } + } +} diff --git a/lib/mainPage.dart b/lib/mainPage.dart index af15e7d..02d696c 100644 --- a/lib/mainPage.dart +++ b/lib/mainPage.dart @@ -1,15 +1,9 @@ -import 'dart:async'; -import 'dart:convert'; import 'package:flutter/material.dart'; -import 'package:http/http.dart' as http; import 'package:auto_size_text/auto_size_text.dart'; import 'package:share/share.dart'; -const dadJokeApi = "https://icanhazdadjoke.com/"; -const httpHeaders = const { - 'User-Agent': 'DadJokes (https://github.com/timsneath/dadjokes)', - 'Accept': 'application/json', -}; +import 'package:dadjokes/jokeserver.dart'; +import 'package:dadjokes/joke.dart'; const jokeTextStyle = const TextStyle( fontFamily: 'Patrick Hand', @@ -17,6 +11,8 @@ const jokeTextStyle = const TextStyle( fontStyle: FontStyle.normal, fontWeight: FontWeight.normal); +String theJoke = ''; + class MainPage extends StatefulWidget { MainPage({Key key, this.title}) : super(key: key); @@ -27,18 +23,17 @@ class MainPage extends StatefulWidget { } class MainPageState extends State { - Future _response; - String _displayedJoke = ''; + Future joke; @override initState() { super.initState(); - _refreshAction(); + joke = JokeServer().fetchJoke(); } _refreshAction() { setState(() { - _response = http.read(dadJokeApi, headers: httpHeaders); + joke = JokeServer().fetchJoke(); }); } @@ -47,69 +42,18 @@ class MainPageState extends State { context: context, builder: (BuildContext context) { return const AlertDialog( - title: Text('About Dad Jokes'), - content: Text( - 'Dad jokes is brought to you by Tim Sneath (@timsneath), ' - 'proud dad of Naomi, Esther, and Silas. May your children ' - 'groan like mine do.\n\nDad jokes come from ' - 'https://icanhazdadjoke.com with thanks.')); + title: Text('About Dad Jokes'), + content: + Text('Dad jokes is brought to you by Tim Sneath (@timsneath), ' + 'proud dad of Naomi, Esther, and Silas. May your children ' + 'groan like mine do.\n\nDad jokes come from ' + 'https://icanhazdadjoke.com with thanks.'), + ); }); } _shareAction() { - if (_displayedJoke.isNotEmpty) { - Share.share(_displayedJoke); - } - } - - FutureBuilder _jokeBody() { - return FutureBuilder( - future: _response, - builder: (BuildContext context, AsyncSnapshot snapshot) { - switch (snapshot.connectionState) { - case ConnectionState.none: - return const ListTile( - leading: Icon(Icons.sync_problem), - title: Text('No connection'), - ); - case ConnectionState.waiting: - return const Center(child: CircularProgressIndicator()); - default: - if (snapshot.hasError) { - return const Center( - child: ListTile( - leading: Icon(Icons.error), - title: Text('Network error'), - subtitle: Text( - 'Sorry - this isn\'t funny, we know, but something went ' - 'wrong when connecting to the Internet. Check your ' - 'network connection and try again.'), - ), - ); - } else { - final decoded = json.decode(snapshot.data); - if (decoded['status'] == 200) { - _displayedJoke = decoded['joke']; - return Padding( - padding: const EdgeInsets.all(16), - child: Dismissible( - key: const Key("joke"), - direction: DismissDirection.horizontal, - onDismissed: (direction) { - _refreshAction(); - }, - child: AutoSizeText(_displayedJoke, style: jokeTextStyle), - )); - } else { - return ListTile( - leading: const Icon(Icons.sync_problem), - title: Text('Unexpected error: ${snapshot.data}'), - ); - } - } - } - }, - ); + Share.share(theJoke); } @override @@ -135,7 +79,12 @@ class MainPageState extends State { ], ), body: Center( - child: SafeArea(child: _jokeBody()), + child: SafeArea( + child: JokeWidget( + joke: joke, + refreshCallback: _refreshAction, + ), + ), ), floatingActionButton: FloatingActionButton.extended( onPressed: _refreshAction, @@ -145,3 +94,53 @@ class MainPageState extends State { ); } } + +class JokeWidget extends StatelessWidget { + final Future joke; + final refreshCallback; + + JokeWidget({Key key, this.joke, this.refreshCallback}) : super(key: key); + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: JokeServer().fetchJoke(), + builder: (BuildContext context, AsyncSnapshot snapshot) { + // We have a joke + if (snapshot.hasData) { + theJoke = snapshot.data.body; + return Padding( + padding: const EdgeInsets.all(16), + child: Dismissible( + key: const Key("joke"), + direction: DismissDirection.horizontal, + onDismissed: (direction) { + refreshCallback(); + }, + child: AutoSizeText( + snapshot.data.body, + style: jokeTextStyle, + ), + ), + ); + } + + // Something went wrong + else if (snapshot.hasError) { + return const Center( + child: ListTile( + leading: Icon(Icons.error), + title: Text('Network error'), + subtitle: + Text('Sorry - this isn\'t funny, we know, but our jokes ' + 'come directly from the Internet for maximum freshness. ' + 'We can\'t reach the server: network issues, perhaps?'), + ), + ); + } + + return CircularProgressIndicator(); + }, + ); + } +}