├── .gitattributes ├── 9781484249710.jpg ├── Contributing.md ├── LICENSE.txt ├── README.md ├── ch_03 ├── Listing_3-01.dart ├── Listing_3-02.dart ├── Listing_3-03.dart ├── Listing_3-04.dart ├── Listing_3-05.dart ├── Listing_3-06.dart ├── Listing_3-07.dart ├── Listing_3-08.dart ├── Listing_3-09.dart └── Listing_3-10.dart ├── ch_04 ├── Listing_4-01.dart ├── Listing_4-02.dart ├── Listing_4-03.dart ├── Listing_4-04.dart ├── Listing_4-05.dart ├── Listing_4-06.dart ├── Listing_4-07.dart └── Listing_4-08.dart ├── ch_05+06 └── flutter_book │ ├── .flutter-plugins-dependencies │ ├── .gitignore │ ├── .metadata │ ├── README.md │ ├── android │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── apress │ │ │ │ └── flutterbook │ │ │ │ └── MainActivity.java │ │ │ └── res │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ └── values │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── settings.gradle │ └── settings_aar.gradle │ ├── ios │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── flutter_export_environment.sh │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ └── contents.xcworkspacedata │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h │ ├── lib │ ├── BaseModel.dart │ ├── appointments │ │ ├── Appointments.dart │ │ ├── AppointmentsDBWorker.dart │ │ ├── AppointmentsEntry.dart │ │ ├── AppointmentsList.dart │ │ └── AppointmentsModel.dart │ ├── contacts │ │ ├── Contacts.dart │ │ ├── ContactsDBWorker.dart │ │ ├── ContactsEntry.dart │ │ ├── ContactsList.dart │ │ └── ContactsModel.dart │ ├── generated │ │ └── i18n.dart │ ├── main.dart │ ├── notes │ │ ├── Notes.dart │ │ ├── NotesDBWorker.dart │ │ ├── NotesEntry.dart │ │ ├── NotesList.dart │ │ └── NotesModel.dart │ ├── tasks │ │ ├── Tasks.dart │ │ ├── TasksDBWorker.dart │ │ ├── TasksEntry.dart │ │ ├── TasksList.dart │ │ └── TasksModel.dart │ └── utils.dart │ ├── pubspec.yaml │ ├── res │ └── values │ │ └── strings_en.arb │ └── test │ └── widget_test.dart ├── ch_07+08 ├── flutter_chat │ ├── .flutter-plugins-dependencies │ ├── .gitignore │ ├── .metadata │ ├── README.md │ ├── android │ │ ├── app │ │ │ ├── build.gradle │ │ │ └── src │ │ │ │ └── main │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── apress │ │ │ │ │ └── flutterchat │ │ │ │ │ └── MainActivity.java │ │ │ │ └── res │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ └── gradle-wrapper.properties │ │ ├── settings.gradle │ │ └── settings_aar.gradle │ ├── assets │ │ ├── drawback01.jpg │ │ ├── private.png │ │ ├── public.png │ │ └── user.png │ ├── ios │ │ ├── Flutter │ │ │ ├── AppFrameworkInfo.plist │ │ │ ├── Debug.xcconfig │ │ │ ├── Release.xcconfig │ │ │ └── flutter_export_environment.sh │ │ ├── Runner.xcodeproj │ │ │ ├── project.pbxproj │ │ │ ├── project.xcworkspace │ │ │ │ └── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ └── xcschemes │ │ │ │ └── Runner.xcscheme │ │ ├── Runner.xcworkspace │ │ │ └── contents.xcworkspacedata │ │ └── Runner │ │ │ ├── AppDelegate.swift │ │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ │ └── LaunchImage.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ └── README.md │ │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ │ ├── Info.plist │ │ │ └── Runner-Bridging-Header.h │ ├── lib │ │ ├── AppDrawer.dart │ │ ├── Connector.dart │ │ ├── CreateRoom.dart │ │ ├── Home.dart │ │ ├── Lobby.dart │ │ ├── LoginDialog.dart │ │ ├── Model.dart │ │ ├── Room.dart │ │ ├── UserList.dart │ │ ├── generated │ │ │ └── i18n.dart │ │ └── main.dart │ ├── pubspec.yaml │ ├── res │ │ └── values │ │ │ └── strings_en.arb │ └── test │ │ └── widget_test.dart └── flutter_chat_server │ ├── nm.bat │ ├── nodemon.json │ ├── package.json │ ├── server.js │ ├── test.html │ └── test.js ├── ch_09 └── flutter_hero │ ├── .gitignore │ ├── .metadata │ ├── README.md │ ├── android │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── apress │ │ │ │ │ └── flutter_hero │ │ │ │ │ └── MainActivity.java │ │ │ └── res │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle │ ├── assets │ ├── alien-0.png │ ├── alien-1.png │ ├── asteroid-0.png │ ├── asteroid-1.png │ ├── background.png │ ├── crystal-0.png │ ├── crystal-1.png │ ├── crystal-2.png │ ├── crystal-3.png │ ├── delivery.mp3 │ ├── explosion-0.png │ ├── explosion-1.png │ ├── explosion-2.png │ ├── explosion-3.png │ ├── explosion-4.png │ ├── explosion.mp3 │ ├── fill.mp3 │ ├── fish-0.png │ ├── fish-1.png │ ├── planet-0.png │ ├── player-0.png │ ├── player-1.png │ ├── robot-0.png │ ├── robot-1.png │ └── thrust.mp3 │ ├── ios │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ └── contents.xcworkspacedata │ └── Runner │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── main.m │ ├── lib │ ├── Enemy.dart │ ├── GameCore.dart │ ├── GameObject.dart │ ├── InputController.dart │ ├── Player.dart │ ├── generated │ │ └── i18n.dart │ └── main.dart │ ├── pubspec.lock │ ├── pubspec.yaml │ ├── res │ └── values │ │ └── strings_en.arb │ └── test │ └── widget_test.dart ├── errata.md └── readme.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /9781484249710.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/9781484249710.jpg -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to Apress Source Code 2 | 3 | Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers. 4 | 5 | ## How to Contribute 6 | 7 | 1. Make sure you have a GitHub account. 8 | 2. Fork the repository for the relevant book. 9 | 3. Create a new branch on which to make your change, e.g. 10 | `git checkout -b my_code_contribution` 11 | 4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted. 12 | 5. Submit a pull request. 13 | 14 | Thank you for your contribution! -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Freeware License, some rights reserved 2 | 3 | Copyright (c) 2019 Frank Zammetti 4 | 5 | Permission is hereby granted, free of charge, to anyone obtaining a copy 6 | of this software and associated documentation files (the "Software"), 7 | to work with the Software within the limits of freeware distribution and fair use. 8 | This includes the rights to use, copy, and modify the Software for personal use. 9 | Users are also allowed and encouraged to submit corrections and modifications 10 | to the Software for the benefit of other users. 11 | 12 | It is not allowed to reuse, modify, or redistribute the Software for 13 | commercial use in any way, or for a user’s educational materials such as books 14 | or blog articles without prior permission from the copyright holder. 15 | 16 | The above copyright notice and this permission notice need to be included 17 | in all copies or substantial portions of the software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS OR APRESS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apress Source Code 2 | 3 | This repository accompanies [*Practical Flutter*](http://www.apress.com/9781484249710) by Frank Zammetti (Apress, 2019). 4 | 5 | [comment]: #cover 6 | ![Cover image](9781484249710.jpg) 7 | 8 | Download the files as a zip using the green button, or clone the repository to your machine using Git. 9 | 10 | ## Releases 11 | 12 | Release v1.0 corresponds to the code in the published book, without corrections or updates. 13 | 14 | ## Contributions 15 | 16 | See the file Contributing.md for more information on how you can contribute to this repository. -------------------------------------------------------------------------------- /ch_03/Listing_3-01.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | void main() => runApp(MyApp()); 4 | 5 | class MyApp extends StatelessWidget { 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | 10 | return MaterialApp(title : "Flutter Playground", 11 | home : Scaffold( 12 | body : Center( 13 | child : Row( 14 | children : [ 15 | Text("Child1"), 16 | Text("Child2"), 17 | Text("Child3") 18 | ] 19 | ) 20 | ) 21 | ) 22 | ); 23 | 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /ch_03/Listing_3-02.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | void main() => runApp(MyApp()); 4 | 5 | class MyApp extends StatelessWidget { 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | 10 | return MaterialApp(title : "Flutter Playground", 11 | home : Scaffold( 12 | body : Center( 13 | child : Card( 14 | child : Column(mainAxisSize: MainAxisSize.min, 15 | children : [ 16 | Text("Child1"), 17 | Divider(), 18 | Text("Child2"), 19 | Divider(), 20 | Text("Child3") 21 | ] 22 | ) 23 | ) 24 | ) 25 | ) 26 | ); 27 | 28 | 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /ch_03/Listing_3-03.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | void main() => runApp(MyApp()); 4 | 5 | class MyApp extends StatelessWidget { 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | 10 | return MaterialApp(title : "Flutter Playground", 11 | home : Scaffold( 12 | appBar : AppBar( 13 | title : Text("Flutter Playground!") 14 | ), 15 | drawer : Drawer( 16 | child : Column( 17 | children : [ 18 | Text("Item 1"), 19 | Divider(), 20 | Text("Item 2"), 21 | Divider(), 22 | Text("Item 3") 23 | ] 24 | ) 25 | ), 26 | body : Center( 27 | child : Row( 28 | children : [ 29 | Text("Child1"), 30 | Text("Child2"), 31 | Text("Child3") 32 | ] 33 | ) 34 | ) 35 | ) 36 | ); 37 | 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /ch_03/Listing_3-04.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | void main() => runApp(MyApp()); 4 | 5 | class MyApp extends StatefulWidget { 6 | MyApp({Key key}) : super(key : key); 7 | @override 8 | _MyApp createState() => _MyApp(); 9 | } 10 | 11 | class _MyApp extends State { 12 | 13 | var _currentPage = 0; 14 | 15 | var _pages = [ 16 | Text("Page 1 - Announcements"), 17 | Text("Page 2 - Birthdays"), 18 | Text("Page 3 - Data") 19 | ]; 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | 24 | return MaterialApp(title : "Flutter Playground", 25 | home : Scaffold( 26 | body : Center(child : _pages.elementAt(_currentPage)), 27 | bottomNavigationBar : BottomNavigationBar( 28 | items : [ 29 | BottomNavigationBarItem( 30 | icon : Icon(Icons.announcement), 31 | title : Text("Announcements") 32 | ), 33 | BottomNavigationBarItem( 34 | icon : Icon(Icons.cake), 35 | title : Text("Birthdays") 36 | ), 37 | BottomNavigationBarItem( 38 | icon : Icon(Icons.cloud), 39 | title : Text("Data") 40 | ), 41 | ], 42 | currentIndex : _currentPage, 43 | fixedColor : Colors.red, 44 | onTap : (int inIndex) { 45 | setState(() { _currentPage = inIndex; }); 46 | } 47 | ) 48 | ) 49 | ); 50 | 51 | } 52 | 53 | } 54 | 55 | -------------------------------------------------------------------------------- /ch_03/Listing_3-05.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | void main() => runApp(MyApp()); 4 | 5 | class MyApp extends StatelessWidget { 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | 10 | return MaterialApp( 11 | home : DefaultTabController( 12 | length : 3, 13 | child : Scaffold( 14 | appBar : AppBar(title : Text("Flutter Playground"), 15 | bottom : TabBar( 16 | tabs : [ 17 | Tab(icon : Icon(Icons.announcement)), 18 | Tab(icon : Icon(Icons.cake)), 19 | Tab(icon : Icon(Icons.cloud)) 20 | ] 21 | ) 22 | ), 23 | body : TabBarView( 24 | children : [ 25 | Center(child : Text("Announcements")), 26 | Center(child : Text("Birthdays")), 27 | Center(child : Text("Data")) 28 | ] 29 | ) 30 | ) 31 | ) 32 | ); 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /ch_03/Listing_3-06.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | void main() => runApp(MyApp()); 4 | 5 | class MyApp extends StatefulWidget { 6 | MyApp({Key key}) : super(key : key); 7 | @override 8 | _MyApp createState() => _MyApp(); 9 | } 10 | 11 | class _MyApp extends State { 12 | 13 | var _currentStep = 0; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return MaterialApp(title : "Flutter Playground", 18 | home : Scaffold( 19 | appBar : AppBar(title : Text("Flutter Playground")), 20 | body : Stepper( 21 | type : StepperType.vertical, currentStep : _currentStep, 22 | onStepContinue : _currentStep < 2 ? () => setState(() => _currentStep += 1) : null, 23 | onStepCancel : _currentStep > 0 ? () => setState(() => _currentStep -= 1) : null, 24 | steps : [ 25 | Step( 26 | title : Text("Get Ready"), isActive : true, 27 | content : Text("Let's begin...") 28 | ), 29 | Step( 30 | title : Text("Get Set"), isActive : true, 31 | content : Text("Ok, just a little more...") 32 | ), 33 | Step( 34 | title : Text("Go!"), isActive : true, 35 | content : Text("And, we're done!") 36 | ) 37 | ] 38 | ) 39 | ) 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ch_03/Listing_3-07.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | void main() => runApp(MyApp()); 4 | 5 | class MyApp extends StatefulWidget { 6 | MyApp({Key key}) : super(key : key); 7 | @override 8 | _MyApp createState() => _MyApp(); 9 | } 10 | 11 | class LoginData { 12 | String username = ""; 13 | String password = ""; 14 | } 15 | 16 | class _MyApp extends State { 17 | 18 | LoginData _loginData = new LoginData(); 19 | GlobalKey _formKey = new GlobalKey(); 20 | 21 | @override 22 | Widget build(BuildContext inContext) { 23 | return MaterialApp(home : Scaffold( 24 | body : Container( 25 | padding : EdgeInsets.all(50.0), 26 | child : Form( 27 | key : this._formKey, 28 | child : Column( 29 | children : [ 30 | TextFormField( 31 | keyboardType : 32 | TextInputType.emailAddress, 33 | validator : (String inValue) { 34 | if (inValue.length == 0) { 35 | return "Please enter username"; 36 | } 37 | return null; 38 | }, 39 | onSaved: (String inValue) { 40 | this._loginData.username = inValue; 41 | }, 42 | decoration : InputDecoration( 43 | hintText : "none@none.com", 44 | labelText : "Username (eMail address)" 45 | ) 46 | ), 47 | TextFormField( 48 | obscureText : true, 49 | validator : (String inValue) { 50 | if (inValue.length < 10) { 51 | return "Password must be >=10 in length"; 52 | } 53 | return null; 54 | }, 55 | onSaved : (String inValue) { 56 | this._loginData.password = inValue; 57 | }, 58 | decoration : InputDecoration( 59 | hintText : "Password", 60 | labelText : "Password" 61 | ) 62 | ), 63 | RaisedButton( 64 | child : Text("Log In!"), 65 | onPressed : () { 66 | if (_formKey.currentState.validate()) { 67 | _formKey.currentState.save(); 68 | print("Username: ${_loginData.username}"); 69 | print("Password: ${_loginData.password}"); 70 | } 71 | } 72 | ) 73 | ] 74 | ) 75 | ) 76 | ) 77 | )); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /ch_03/Listing_3-08.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | void main() => runApp(MyApp()); 4 | 5 | class MyApp extends StatefulWidget { 6 | MyApp({Key key}) : super(key : key); 7 | @override 8 | _MyApp createState() => _MyApp(); 9 | } 10 | 11 | class _MyApp extends State { 12 | 13 | GlobalKey _formKey = new GlobalKey(); 14 | var _checkboxValue = false; 15 | var _switchValue = false; 16 | var _sliderValue = .3; 17 | var _radioValue = 1; 18 | 19 | @override 20 | Widget build(BuildContext inContext) { 21 | return MaterialApp(home : Scaffold( 22 | body : Container( 23 | padding : EdgeInsets.all(50.0), 24 | child : Form( 25 | key : this._formKey, 26 | child : Column( 27 | children : [ 28 | Checkbox( 29 | value : _checkboxValue, 30 | onChanged : (bool inValue) { 31 | setState(() { _checkboxValue = inValue; }); 32 | } 33 | ), 34 | Switch( 35 | value : _switchValue, 36 | onChanged : (bool inValue) { 37 | setState(() { _switchValue = inValue; }); 38 | } 39 | ), 40 | Slider( 41 | min : 0, max : 20, 42 | value : _sliderValue, 43 | onChanged : (inValue) { 44 | setState(() => _sliderValue = inValue); 45 | } 46 | ), 47 | Row(children : [ 48 | Radio(value : 1, groupValue : _radioValue, 49 | onChanged : (int inValue) { 50 | setState(() { _radioValue = inValue; }); 51 | } 52 | ), 53 | Text("Option 1") 54 | ]), 55 | Row(children : [ 56 | Radio(value : 2, groupValue : _radioValue, 57 | onChanged : (int inValue) { 58 | setState(() { _radioValue = inValue; }); 59 | } 60 | ), 61 | Text("Option 2") 62 | ]), 63 | Row(children : [ 64 | Radio(value : 3, groupValue : _radioValue, 65 | onChanged : (int inValue) { 66 | setState(() { _radioValue = inValue; }); 67 | } 68 | ), 69 | Text("Option 3") 70 | ]) 71 | ] 72 | ) 73 | ) 74 | ) 75 | )); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /ch_03/Listing_3-09.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | void main() => runApp(MyApp()); 4 | 5 | class MyApp extends StatelessWidget { 6 | @override 7 | Widget build(BuildContext context) { 8 | return MaterialApp(home : Scaffold(body : Home())); 9 | } 10 | } 11 | 12 | class Home extends StatelessWidget { 13 | 14 | Future _selectDate(inContext) async { 15 | DateTime selectedDate = await showDatePicker( 16 | context : inContext, 17 | initialDate : DateTime.now(), 18 | firstDate : DateTime(2017), 19 | lastDate : DateTime(2021) 20 | ); 21 | print(selectedDate); 22 | } 23 | 24 | Future _selectTime(inContext) async { 25 | TimeOfDay selectedTime = await showTimePicker( 26 | context : inContext, 27 | initialTime : TimeOfDay.now(), 28 | ); 29 | print(selectedTime); 30 | } 31 | 32 | @override 33 | Widget build(BuildContext inContext) { 34 | return Scaffold( 35 | body : Column( 36 | children : [ 37 | Container(height : 50), 38 | RaisedButton( 39 | child : Text("Test DatePicker"), 40 | onPressed : () => _selectDate(inContext) 41 | ), 42 | RaisedButton( 43 | child : Text("Test TimePicker"), 44 | onPressed : () => _selectTime(inContext) 45 | ) 46 | ] 47 | ) 48 | ); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /ch_03/Listing_3-10.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | void main() => runApp(MyApp()); 4 | 5 | class MyApp extends StatelessWidget { 6 | @override 7 | Widget build(BuildContext context) { 8 | return MaterialApp(home : Scaffold(body : Home())); 9 | } 10 | } 11 | 12 | class Home extends StatelessWidget { 13 | 14 | @override 15 | Widget build(BuildContext inContext) { 16 | 17 | Future _showIt() async { 18 | switch (await showDialog( 19 | context : inContext, 20 | builder : (BuildContext inContext) { 21 | return SimpleDialog( 22 | title : Text("What's your favorite food?"), 23 | children : [ 24 | SimpleDialogOption( 25 | onPressed : () { 26 | Navigator.pop(inContext, "brocolli"); 27 | }, 28 | child : Text("Brocolli") 29 | ), 30 | SimpleDialogOption( 31 | onPressed : () { 32 | Navigator.pop(inContext, "steak"); 33 | }, 34 | child : Text("Steak") 35 | ) 36 | ] 37 | ); 38 | } 39 | )) { 40 | case "brocolli": print("Brocolli"); break; 41 | case "steak": print("Steak"); break; 42 | } 43 | } 44 | 45 | return Scaffold( 46 | body : Center( 47 | child : RaisedButton( 48 | child : Text("Show it"), 49 | onPressed : _showIt 50 | ) 51 | ) 52 | ); 53 | 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /ch_04/Listing_4-01.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | void main() => runApp(MyApp()); 4 | 5 | class MyApp extends StatelessWidget { 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | 10 | return MaterialApp(title : "Flutter Playground", 11 | home : Scaffold( 12 | body : Column(children : [ 13 | Spacer(), 14 | Center(child : Opacity(opacity: .25, child : Text("Faded") )), 15 | Spacer(), 16 | Center(child : Theme ( 17 | data : ThemeData( accentColor : Colors.red ), 18 | child : Container( 19 | color : Theme.of(context).accentColor, 20 | child : Text( 21 | "Text with a background color", 22 | style : Theme.of(context).textTheme.title, 23 | ) 24 | ) 25 | )), 26 | Spacer(), 27 | Center(child : DecoratedBox( 28 | decoration : BoxDecoration( 29 | gradient : LinearGradient( 30 | begin : Alignment.topCenter, 31 | end : Alignment.bottomCenter, 32 | colors : [ Color(0xFF000000), Color(0xFFFF0000) ], 33 | tileMode : TileMode.repeated 34 | ) 35 | ), 36 | child : Container(width : 100, height : 100, 37 | child : Text("Hello", 38 | style : TextStyle(color : Colors.white) 39 | ) 40 | ) 41 | )), 42 | Spacer(), 43 | Center(child : Container( 44 | color : Colors.yellow, 45 | child : Transform( 46 | alignment : Alignment.bottomLeft, 47 | transform : Matrix4.skewY(0.4)..rotateZ(-3 / 12.0), 48 | child : Container( 49 | padding : const EdgeInsets.all(12.0), 50 | color : Colors.red, 51 | child : Text("Eat at Joe's!") 52 | ) 53 | ) 54 | )), 55 | Spacer() 56 | ]) 57 | ) 58 | ); 59 | 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /ch_04/Listing_4-02.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | void main() => runApp(MyApp()); 4 | 5 | class MyApp extends StatelessWidget { 6 | 7 | Widget build(BuildContext inContext) { 8 | return MaterialApp(home : Scaffold( 9 | body : Column(children : [ 10 | Container(height : 100), 11 | Table( 12 | border : TableBorder( 13 | top : BorderSide(width : 2), 14 | bottom : BorderSide(width : 2), 15 | left : BorderSide(width : 2), 16 | right : BorderSide(width : 2) 17 | ), 18 | children : [ 19 | TableRow( 20 | children : [ 21 | Center(child : Padding( 22 | padding : EdgeInsets.all(10), 23 | child : Text("1")) 24 | ), 25 | Center(child : Padding( 26 | padding : EdgeInsets.all(10), 27 | child : Text("2")) 28 | ), 29 | Center(child : Padding( 30 | padding : EdgeInsets.all(10), 31 | child : Text("3")) 32 | ) 33 | ] 34 | ) 35 | ] 36 | ) 37 | ]) 38 | )); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /ch_04/Listing_4-03.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | void main() => runApp(MyApp()); 4 | 5 | class MyApp extends StatelessWidget { 6 | 7 | Widget build(BuildContext inContext) { 8 | return MaterialApp(home : Scaffold( 9 | body : Column(children : [ 10 | Container(height : 100), 11 | DataTable(sortColumnIndex : 1, 12 | columns : [ 13 | DataColumn(label : Text("First Name")), 14 | DataColumn(label : Text("Last Name")) 15 | ], 16 | rows : [ 17 | DataRow(cells : [ 18 | DataCell(Text("Leia")), 19 | DataCell(Text("Organa"), showEditIcon : true) 20 | ]), 21 | DataRow(cells : [ 22 | DataCell(Text("Luke")), 23 | DataCell(Text("Skywalker")) 24 | ]), 25 | DataRow(cells : [ 26 | DataCell(Text("Han")), 27 | DataCell(Text("Solo")) 28 | ]) 29 | ] 30 | ) 31 | ]) 32 | )); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /ch_04/Listing_4-04.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | void main() => runApp(MyApp()); 4 | 5 | class MyApp extends StatelessWidget { 6 | 7 | @override 8 | Widget build(BuildContext inContext) { 9 | return MaterialApp(home : Scaffold( 10 | body : GridView.count( 11 | padding : EdgeInsets.all(4.0), 12 | crossAxisCount : 4, childAspectRatio : 1.0, 13 | mainAxisSpacing : 4.0, crossAxisSpacing : 4.0, 14 | children: [ 15 | GridTile(child : new FlutterLogo()), 16 | GridTile(child : new FlutterLogo()), 17 | GridTile(child : new FlutterLogo()), 18 | GridTile(child : new FlutterLogo()), 19 | GridTile(child : new FlutterLogo()), 20 | GridTile(child : new FlutterLogo()), 21 | GridTile(child : new FlutterLogo()), 22 | GridTile(child : new FlutterLogo()), 23 | GridTile(child : new FlutterLogo()) 24 | ] 25 | ) 26 | )); 27 | } 28 | 29 | } 30 | 31 | -------------------------------------------------------------------------------- /ch_04/Listing_4-05.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | void main() => runApp(MyApp()); 4 | 5 | class MyApp extends StatelessWidget { 6 | 7 | @override 8 | Widget build(BuildContext inContext) { 9 | return MaterialApp(home : Scaffold( 10 | body : ListView( 11 | children : [ 12 | ListTile(leading : Icon(Icons.gif), title : Text("1")), 13 | ListTile(leading : Icon(Icons.book), title : Text("2")), 14 | ListTile(leading : Icon(Icons.call), title : Text("3")), 15 | ListTile(leading : Icon(Icons.dns), title : Text("4")), 16 | ListTile(leading : Icon(Icons.cake), title : Text("5")), 17 | ListTile(leading : Icon(Icons.pets), title : Text("6")), 18 | ListTile(leading : Icon(Icons.poll), title : Text("7")), 19 | ListTile(leading : Icon(Icons.face), title : Text("8")), 20 | ListTile(leading : Icon(Icons.home), title : Text("9")), 21 | ListTile(leading : Icon(Icons.adb), title : Text("10")), 22 | ListTile(leading : Icon(Icons.dvr), title : Text("11")), 23 | ListTile(leading : Icon(Icons.hd), title : Text("12")), 24 | ListTile(leading : Icon(Icons.toc), title : Text("3")), 25 | ListTile(leading : Icon(Icons.tv), title : Text("14")), 26 | ListTile(leading : Icon(Icons.help), title : Text("15")) 27 | ] 28 | ) 29 | )); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /ch_04/Listing_4-06.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | void main() => runApp(MyApp()); 4 | 5 | class MyApp extends StatelessWidget { 6 | 7 | @override 8 | Widget build(BuildContext inContext) { 9 | return MaterialApp(home : Scaffold( 10 | body : Center(child : 11 | Chip( 12 | avatar : CircleAvatar( 13 | backgroundImage : AssetImage("img/ron.jpg") 14 | ), 15 | backgroundColor : Colors.grey.shade300, 16 | label : Text("Frank Zammetti") 17 | ) 18 | ) 19 | 20 | )); 21 | } 22 | 23 | } 24 | 25 | -------------------------------------------------------------------------------- /ch_04/Listing_4-07.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | void main() => runApp(MyApp()); 4 | 5 | class MyApp extends StatelessWidget { 6 | 7 | @override 8 | Widget build(BuildContext inContext) { 9 | return MaterialApp(home : Scaffold( 10 | floatingActionButton : FloatingActionButton( 11 | backgroundColor : Colors.red, 12 | foregroundColor : Colors.yellow, 13 | child : Icon(Icons.add), 14 | onPressed : () { print("Ouch! Stop it!"); } 15 | ), 16 | body : Center(child : Text("Click it!")) 17 | 18 | )); 19 | } 20 | 21 | } 22 | 23 | -------------------------------------------------------------------------------- /ch_04/Listing_4-08.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | void main() => runApp(MyApp()); 4 | 5 | class MyApp extends StatelessWidget { 6 | 7 | @override 8 | Widget build(BuildContext inContext) { 9 | return MaterialApp(home : Scaffold( 10 | body : Center(child : 11 | PopupMenuButton( 12 | onSelected : (String result) { print(result); }, 13 | itemBuilder : (BuildContext context) => >[ 14 | PopupMenuItem(value : "copy", child : Text("Copy")), 15 | PopupMenuItem(value : "cut", child : Text("Cut")), 16 | PopupMenuItem(value : "paste", child : Text("Paste")) 17 | ] 18 | ) 19 | ) 20 | 21 | )); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/.flutter-plugins-dependencies: -------------------------------------------------------------------------------- 1 | {"_info":"// This is a generated file; do not edit or check into version control.","dependencyGraph":[{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"image_picker","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"path_provider","dependencies":[]},{"name":"sqflite","dependencies":[]}]} -------------------------------------------------------------------------------- /ch_05+06/flutter_book/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.lock 4 | *.log 5 | *.pyc 6 | *.swp 7 | .DS_Store 8 | .atom/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # Visual Studio Code related 20 | .vscode/ 21 | 22 | # Flutter/Dart/Pub related 23 | **/doc/api/ 24 | .dart_tool/ 25 | .flutter-plugins 26 | .packages 27 | .pub-cache/ 28 | .pub/ 29 | build/ 30 | 31 | # Android related 32 | **/android/**/gradle-wrapper.jar 33 | **/android/.gradle 34 | **/android/captures/ 35 | **/android/gradlew 36 | **/android/gradlew.bat 37 | **/android/local.properties 38 | **/android/**/GeneratedPluginRegistrant.java 39 | 40 | # iOS/XCode related 41 | **/ios/**/*.mode1v3 42 | **/ios/**/*.mode2v3 43 | **/ios/**/*.moved-aside 44 | **/ios/**/*.pbxuser 45 | **/ios/**/*.perspectivev3 46 | **/ios/**/*sync/ 47 | **/ios/**/.sconsign.dblite 48 | **/ios/**/.tags* 49 | **/ios/**/.vagrant/ 50 | **/ios/**/DerivedData/ 51 | **/ios/**/Icon? 52 | **/ios/**/Pods/ 53 | **/ios/**/.symlinks/ 54 | **/ios/**/profile 55 | **/ios/**/xcuserdata 56 | **/ios/.generated/ 57 | **/ios/Flutter/App.framework 58 | **/ios/Flutter/Flutter.framework 59 | **/ios/Flutter/Generated.xcconfig 60 | **/ios/Flutter/app.flx 61 | **/ios/Flutter/app.zip 62 | **/ios/Flutter/flutter_assets/ 63 | **/ios/ServiceDefinitions.json 64 | **/ios/Runner/GeneratedPluginRegistrant.* 65 | 66 | # Exceptions to above rules. 67 | !**/ios/**/default.mode1v3 68 | !**/ios/**/default.mode2v3 69 | !**/ios/**/default.pbxuser 70 | !**/ios/**/default.perspectivev3 71 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 72 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 5391447fae6209bb21a89e6a5a6583cac1af9b4b 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/README.md: -------------------------------------------------------------------------------- 1 | # flutter_book 2 | 3 | flutter_book 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.io/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.io/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.io/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | android { 28 | compileSdkVersion 28 29 | 30 | lintOptions { 31 | disable 'InvalidPackage' 32 | } 33 | 34 | defaultConfig { 35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 36 | applicationId "com.apress.flutterbook" 37 | minSdkVersion 16 38 | targetSdkVersion 28 39 | versionCode flutterVersionCode.toInteger() 40 | versionName flutterVersionName 41 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 42 | } 43 | 44 | buildTypes { 45 | release { 46 | // TODO: Add your own signing config for the release build. 47 | // Signing with the debug keys for now, so `flutter run --release` works. 48 | signingConfig signingConfigs.debug 49 | } 50 | } 51 | } 52 | 53 | flutter { 54 | source '../..' 55 | } 56 | 57 | dependencies { 58 | testImplementation 'junit:junit:4.12' 59 | androidTestImplementation 'androidx.test:runner:1.2.0' 60 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 61 | } 62 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 15 | 19 | 26 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/android/app/src/main/java/com/apress/flutterbook/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.apress.flutterbook; 2 | 3 | import android.os.Bundle; 4 | import io.flutter.app.FlutterActivity; 5 | import io.flutter.plugins.GeneratedPluginRegistrant; 6 | 7 | public class MainActivity extends FlutterActivity { 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | GeneratedPluginRegistrant.registerWith(this); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.3.0' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=false 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip 7 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/android/settings_aar.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Flutter/flutter_export_environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is a generated file; do not edit or check into version control. 3 | export "FLUTTER_ROOT=C:\library\flutter" 4 | export "FLUTTER_APPLICATION_PATH=C:\temp\practical-flutter\ch_05+06\flutter_book" 5 | export "FLUTTER_TARGET=lib\main.dart" 6 | export "FLUTTER_BUILD_DIR=build" 7 | export "SYMROOT=${SOURCE_ROOT}/../build\ios" 8 | export "FLUTTER_FRAMEWORK_DIR=C:\library\flutter\bin\cache\artifacts\engine\ios" 9 | export "FLUTTER_BUILD_NAME=1.0.0" 10 | export "FLUTTER_BUILD_NUMBER=1" 11 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | flutter_book 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /ch_05+06/flutter_book/lib/BaseModel.dart: -------------------------------------------------------------------------------- 1 | import "package:scoped_model/scoped_model.dart"; 2 | 3 | 4 | /// ******************************************************************************************************************** 5 | /// Base class that the model for all entities extend. 6 | /// ******************************************************************************************************************** 7 | class BaseModel extends Model { 8 | 9 | 10 | /// Which page of the stack is currently showing. 11 | int stackIndex = 0; 12 | 13 | 14 | /// The list of entities. 15 | List entityList = [ ]; 16 | 17 | 18 | /// The entity being edited. 19 | var entityBeingEdited; 20 | 21 | 22 | /// The date chosen by the user. Needed to be able to display what the user picks on the entry screen. 23 | String chosenDate; 24 | 25 | 26 | /// For display of the date chosen by the user. 27 | /// 28 | /// @param inDate The date in MM/DD/YYYY form. 29 | void setChosenDate(String inDate) { 30 | 31 | print("## BaseModel.setChosenDate(): inDate = $inDate"); 32 | 33 | chosenDate = inDate; 34 | notifyListeners(); 35 | 36 | } /* End setChosenDate(). */ 37 | 38 | 39 | /// Load data from database. 40 | /// 41 | /// @param inEntityType The type of entity being loaded ("appointments", "contacts", "notes" or "tasks"). 42 | /// @param inDatabase The DBProvider.db instance for the entity. 43 | void loadData(String inEntityType, dynamic inDatabase) async { 44 | 45 | print("## ${inEntityType}Model.loadData()"); 46 | 47 | // Load entities from database. 48 | entityList = await inDatabase.getAll(); 49 | 50 | // Notify listeners that the data is available so they can paint themselves. 51 | notifyListeners(); 52 | 53 | } /* End loadData(). */ 54 | 55 | 56 | /// For navigating between entry and list views. 57 | /// 58 | /// @param inStackIndex The stack index to make current. 59 | void setStackIndex(int inStackIndex) { 60 | 61 | print("## BaseModel.setStackIndex(): inStackIndex = $inStackIndex"); 62 | 63 | stackIndex = inStackIndex; 64 | notifyListeners(); 65 | 66 | } /* End setStackIndex(). */ 67 | 68 | 69 | } /* End class. */ 70 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/lib/appointments/Appointments.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | import "package:scoped_model/scoped_model.dart"; 3 | import "AppointmentsDBWorker.dart"; 4 | import "AppointmentsList.dart"; 5 | import "AppointmentsEntry.dart"; 6 | import "AppointmentsModel.dart" show AppointmentsModel, appointmentsModel; 7 | 8 | 9 | /// ******************************************************************************************************************** 10 | /// The Appointments screen. 11 | /// ******************************************************************************************************************** 12 | class Appointments extends StatelessWidget { 13 | 14 | 15 | /// Constructor. 16 | Appointments() { 17 | 18 | print("## Appointments.constructor"); 19 | 20 | // Initial load of data. 21 | appointmentsModel.loadData("appointments", AppointmentsDBWorker.db); 22 | 23 | } /* End constructor. */ 24 | 25 | 26 | /// The build() method. 27 | /// 28 | /// @param inContext The BuildContext for this widget. 29 | /// @return A Widget. 30 | Widget build(BuildContext inContext) { 31 | 32 | print("## Appointments.build()"); 33 | 34 | return ScopedModel( 35 | model : appointmentsModel, 36 | child : ScopedModelDescendant( 37 | builder : (BuildContext inContext, Widget inChild, AppointmentsModel inModel) { 38 | return IndexedStack( 39 | index : inModel.stackIndex, 40 | children : [ 41 | AppointmentsList(), 42 | AppointmentsEntry() 43 | ] /* End IndexedStack children. */ 44 | ); /* End IndexedStack. */ 45 | } /* End ScopedModelDescendant builder(). */ 46 | ) /* End ScopedModelDescendant. */ 47 | ); /* End ScopedModel. */ 48 | 49 | } /* End build(). */ 50 | 51 | 52 | } /* End class. */ 53 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/lib/appointments/AppointmentsModel.dart: -------------------------------------------------------------------------------- 1 | import "../BaseModel.dart"; 2 | 3 | 4 | /// A class representing this PIM entity type. 5 | class Appointment { 6 | 7 | 8 | /// The fields this entity type contains. 9 | int id; 10 | String title; 11 | String description; 12 | String apptDate; // YYYY,MM,DD 13 | String apptTime; // HH,MM 14 | 15 | 16 | /// Just for debugging, so we get something useful in the console. 17 | String toString() { 18 | return "{ id=$id, title=$title, description=$description, apptDate=$apptDate, apptDate=$apptTime }"; 19 | } 20 | 21 | 22 | } /* End class. */ 23 | 24 | 25 | /// ******************************************************************************************************************** 26 | /// The model backing this entity type's views. 27 | /// ******************************************************************************************************************** 28 | class AppointmentsModel extends BaseModel { 29 | 30 | 31 | /// The appointment time. Needed to be able to display what the user picks in the Text widget on the entry screen. 32 | String apptTime; 33 | 34 | 35 | /// For display of the appointment time chosen by the user. 36 | /// 37 | /// @param inApptTime The appointment date in HH:MM form. 38 | void setApptTime(String inApptTime) { 39 | 40 | apptTime = inApptTime; 41 | notifyListeners(); 42 | 43 | } /* End setApptTime(). */ 44 | 45 | 46 | } /* End class. */ 47 | 48 | 49 | // The one and only instance of this model. 50 | AppointmentsModel appointmentsModel = AppointmentsModel(); 51 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/lib/contacts/Contacts.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | import "package:scoped_model/scoped_model.dart"; 3 | import "ContactsDBWorker.dart"; 4 | import "ContactsList.dart"; 5 | import "ContactsEntry.dart"; 6 | import "ContactsModel.dart" show ContactsModel, contactsModel; 7 | 8 | 9 | /// ******************************************************************************************************************** 10 | /// The Contacts screen. 11 | /// ******************************************************************************************************************** 12 | class Contacts extends StatelessWidget { 13 | 14 | 15 | /// Constructor. 16 | Contacts() { 17 | 18 | print("## Contacts.constructor"); 19 | 20 | // Initial load of data. 21 | contactsModel.loadData("contacts", ContactsDBWorker.db); 22 | 23 | } /* End constructor. */ 24 | 25 | 26 | /// The build() method. 27 | /// 28 | /// @param inContext The BuildContext for this widget. 29 | /// @return A Widget. 30 | Widget build(BuildContext inContext) { 31 | 32 | print("## Contacts.build()"); 33 | 34 | return ScopedModel( 35 | model : contactsModel, 36 | child : ScopedModelDescendant( 37 | builder : (BuildContext inContext, Widget inChild, ContactsModel inModel) { 38 | return IndexedStack( 39 | index : inModel.stackIndex, 40 | children : [ 41 | ContactsList(), 42 | ContactsEntry() 43 | ] /* End IndexedStack children. */ 44 | ); /* End IndexedStack. */ 45 | } /* End ScopedModelDescendant builder(). */ 46 | ) /* End ScopedModelDescendant. */ 47 | ); /* End ScopedModel. */ 48 | 49 | } /* End build(). */ 50 | 51 | 52 | } /* End class. */ 53 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/lib/contacts/ContactsModel.dart: -------------------------------------------------------------------------------- 1 | import "../BaseModel.dart"; 2 | 3 | 4 | /// A class representing this PIM entity type. 5 | class Contact { 6 | 7 | 8 | /// The fields this entity type contains. 9 | int id; 10 | String name; 11 | String phone; 12 | String email; 13 | String birthday; // YYYY,MM,DD 14 | 15 | 16 | /// Just for debugging, so we get something useful in the console. 17 | String toString() { 18 | return "{ id=$id, name=$name, phone=$phone, email=$email, birthday=$birthday }"; 19 | } 20 | 21 | 22 | } /* End class. */ 23 | 24 | 25 | /// ******************************************************************************************************************** 26 | /// The model backing this entity type's views. 27 | /// ******************************************************************************************************************** 28 | class ContactsModel extends BaseModel { 29 | 30 | 31 | /// "Force" a rebuild of the entry page (when selecting an avatar image). 32 | void triggerRebuild() { 33 | 34 | print("## ContactsModel.triggerRebuild()"); 35 | 36 | notifyListeners(); 37 | 38 | } /* End triggerRebuild(). */ 39 | 40 | 41 | } /* End class. */ 42 | 43 | 44 | // The one and only instance of this model. 45 | ContactsModel contactsModel = ContactsModel(); 46 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/lib/generated/i18n.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | // ignore_for_file: non_constant_identifier_names 7 | // ignore_for_file: camel_case_types 8 | // ignore_for_file: prefer_single_quotes 9 | 10 | // This file is automatically generated. DO NOT EDIT, all your changes would be lost. 11 | class S implements WidgetsLocalizations { 12 | const S(); 13 | 14 | static S current; 15 | 16 | static const GeneratedLocalizationsDelegate delegate = 17 | GeneratedLocalizationsDelegate(); 18 | 19 | static S of(BuildContext context) => Localizations.of(context, S); 20 | 21 | @override 22 | TextDirection get textDirection => TextDirection.ltr; 23 | 24 | } 25 | 26 | class $en extends S { 27 | const $en(); 28 | } 29 | 30 | class GeneratedLocalizationsDelegate extends LocalizationsDelegate { 31 | const GeneratedLocalizationsDelegate(); 32 | 33 | List get supportedLocales { 34 | return const [ 35 | Locale("en", ""), 36 | ]; 37 | } 38 | 39 | LocaleListResolutionCallback listResolution({Locale fallback, bool withCountry = true}) { 40 | return (List locales, Iterable supported) { 41 | if (locales == null || locales.isEmpty) { 42 | return fallback ?? supported.first; 43 | } else { 44 | return _resolve(locales.first, fallback, supported, withCountry); 45 | } 46 | }; 47 | } 48 | 49 | LocaleResolutionCallback resolution({Locale fallback, bool withCountry = true}) { 50 | return (Locale locale, Iterable supported) { 51 | return _resolve(locale, fallback, supported, withCountry); 52 | }; 53 | } 54 | 55 | @override 56 | Future load(Locale locale) { 57 | final String lang = getLang(locale); 58 | if (lang != null) { 59 | switch (lang) { 60 | case "en": 61 | S.current = const $en(); 62 | return SynchronousFuture(S.current); 63 | default: 64 | // NO-OP. 65 | } 66 | } 67 | S.current = const S(); 68 | return SynchronousFuture(S.current); 69 | } 70 | 71 | @override 72 | bool isSupported(Locale locale) => _isSupported(locale, true); 73 | 74 | @override 75 | bool shouldReload(GeneratedLocalizationsDelegate old) => false; 76 | 77 | /// 78 | /// Internal method to resolve a locale from a list of locales. 79 | /// 80 | Locale _resolve(Locale locale, Locale fallback, Iterable supported, bool withCountry) { 81 | if (locale == null || !_isSupported(locale, withCountry)) { 82 | return fallback ?? supported.first; 83 | } 84 | 85 | final Locale languageLocale = Locale(locale.languageCode, ""); 86 | if (supported.contains(locale)) { 87 | return locale; 88 | } else if (supported.contains(languageLocale)) { 89 | return languageLocale; 90 | } else { 91 | final Locale fallbackLocale = fallback ?? supported.first; 92 | return fallbackLocale; 93 | } 94 | } 95 | 96 | /// 97 | /// Returns true if the specified locale is supported, false otherwise. 98 | /// 99 | bool _isSupported(Locale locale, bool withCountry) { 100 | if (locale != null) { 101 | for (Locale supportedLocale in supportedLocales) { 102 | // Language must always match both locales. 103 | if (supportedLocale.languageCode != locale.languageCode) { 104 | continue; 105 | } 106 | 107 | // If country code matches, return this locale. 108 | if (supportedLocale.countryCode == locale.countryCode) { 109 | return true; 110 | } 111 | 112 | // If no country requirement is requested, check if this locale has no country. 113 | if (true != withCountry && (supportedLocale.countryCode == null || supportedLocale.countryCode.isEmpty)) { 114 | return true; 115 | } 116 | } 117 | } 118 | return false; 119 | } 120 | } 121 | 122 | String getLang(Locale l) => l == null 123 | ? null 124 | : l.countryCode != null && l.countryCode.isEmpty 125 | ? l.languageCode 126 | : l.toString(); 127 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/lib/main.dart: -------------------------------------------------------------------------------- 1 | import "dart:io"; 2 | import "package:flutter/material.dart"; 3 | import "package:path_provider/path_provider.dart"; 4 | import "appointments/Appointments.dart"; 5 | import "contacts/Contacts.dart"; 6 | import "notes/Notes.dart"; 7 | import "tasks/Tasks.dart"; 8 | import "utils.dart" as utils; 9 | 10 | 11 | /// Start it up! 12 | void main() { 13 | 14 | WidgetsFlutterBinding.ensureInitialized(); 15 | 16 | print("## main(): FlutterBook Starting"); 17 | 18 | startMeUp() async { 19 | Directory docsDir = await getApplicationDocumentsDirectory(); 20 | utils.docsDir = docsDir; 21 | runApp(FlutterBook()); 22 | } 23 | 24 | startMeUp(); 25 | 26 | } /* End main(). */ 27 | 28 | 29 | /// ******************************************************************************************************************** 30 | /// Main app widget. 31 | /// ******************************************************************************************************************** 32 | class FlutterBook extends StatelessWidget { 33 | 34 | 35 | /// The build() method. 36 | /// 37 | /// @param inContext The BuildContext for this widget. 38 | /// @return A Widget. 39 | Widget build(BuildContext inContext) { 40 | 41 | print("## FlutterBook.build()"); 42 | 43 | return MaterialApp( 44 | home : DefaultTabController( 45 | length : 4, 46 | child : Scaffold( 47 | appBar : AppBar( 48 | title : Text("FlutterBook"), 49 | bottom : TabBar( 50 | tabs : [ 51 | Tab(icon : Icon(Icons.date_range), text : "Appointments"), 52 | Tab(icon : Icon(Icons.contacts), text : "Contacts"), 53 | Tab(icon : Icon(Icons.note), text : "Notes"), 54 | Tab(icon : Icon(Icons.assignment_turned_in), text : "Tasks") 55 | ] /* End TabBar.tabs. */ 56 | ) /* End TabBar. */ 57 | ), /* End AppBar. */ 58 | body : TabBarView( 59 | children : [ 60 | Appointments(), 61 | Contacts(), 62 | Notes(), 63 | Tasks() 64 | ] /* End TabBarView.children. */ 65 | ) /* End TabBarView. */ 66 | ) /* End Scaffold. */ 67 | ) /* End DefaultTabController. */ 68 | ); /* End MaterialApp. */ 69 | 70 | } /* End build(). */ 71 | 72 | 73 | } /* End class. */ 74 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/lib/notes/Notes.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | import "package:scoped_model/scoped_model.dart"; 3 | import "NotesDBWorker.dart"; 4 | import "NotesList.dart"; 5 | import "NotesEntry.dart"; 6 | import "NotesModel.dart" show NotesModel, notesModel; 7 | 8 | 9 | /// ******************************************************************************************************************** 10 | /// The Notes screen. 11 | /// ******************************************************************************************************************** 12 | class Notes extends StatelessWidget { 13 | 14 | 15 | /// Constructor. 16 | Notes() { 17 | 18 | print("## Notes.constructor"); 19 | 20 | // Initial load of data. 21 | notesModel.loadData("notes", NotesDBWorker.db); 22 | 23 | } /* End constructor. */ 24 | 25 | 26 | /// The build() method. 27 | /// 28 | /// @param inContext The BuildContext for this widget. 29 | /// @return A Widget. 30 | Widget build(BuildContext inContext) { 31 | 32 | print("## Notes.build()"); 33 | 34 | return ScopedModel( 35 | model : notesModel, 36 | child : ScopedModelDescendant( 37 | builder : (BuildContext inContext, Widget inChild, NotesModel inModel) { 38 | return IndexedStack( 39 | index : inModel.stackIndex, 40 | children : [ 41 | NotesList(), 42 | NotesEntry() 43 | ] /* End IndexedStack children. */ 44 | ); /* End IndexedStack. */ 45 | } /* End ScopedModelDescendant builder(). */ 46 | ) /* End ScopedModelDescendant. */ 47 | ); /* End ScopedModel. */ 48 | 49 | } /* End build(). */ 50 | 51 | 52 | } /* End class. */ 53 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/lib/notes/NotesModel.dart: -------------------------------------------------------------------------------- 1 | import "../BaseModel.dart"; 2 | 3 | 4 | /// A class representing this PIM entity type. 5 | class Note { 6 | 7 | 8 | /// The fields this entity type contains. 9 | int id; 10 | String title; 11 | String content; 12 | String color; 13 | 14 | 15 | /// Just for debugging, so we get something useful in the console. 16 | String toString() { 17 | return "{ id=$id, title=$title, content=$content, color=$color }"; 18 | } 19 | 20 | 21 | } /* End class. */ 22 | 23 | 24 | /// **************************************************************************** 25 | /// The model backing this entity type's views. 26 | /// **************************************************************************** 27 | class NotesModel extends BaseModel { 28 | 29 | 30 | /// The color. Needed to be able to display what the user picks in the Text widget on the entry screen. 31 | String color; 32 | 33 | 34 | /// For display of the color chosen by the user. 35 | /// 36 | /// @param inColor The color. 37 | void setColor(String inColor) { 38 | 39 | print("## NotesModel.setColor(): inColor = $inColor"); 40 | 41 | color = inColor; 42 | notifyListeners(); 43 | 44 | } /* End setColor(). */ 45 | 46 | 47 | } /* End class. */ 48 | 49 | 50 | // The one and only instance of this model. 51 | NotesModel notesModel = NotesModel(); 52 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/lib/tasks/Tasks.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | import "package:scoped_model/scoped_model.dart"; 3 | import "TasksDBWorker.dart"; 4 | import "TasksList.dart"; 5 | import "TasksEntry.dart"; 6 | import "TasksModel.dart" show TasksModel, tasksModel; 7 | 8 | 9 | /// ******************************************************************************************************************** 10 | /// The Tasks screen. 11 | /// ******************************************************************************************************************** 12 | class Tasks extends StatelessWidget { 13 | 14 | 15 | /// Constructor. 16 | Tasks() { 17 | 18 | print("## Tasks.constructor"); 19 | 20 | // Initial load of data. 21 | tasksModel.loadData("tasks", TasksDBWorker.db); 22 | 23 | } /* End constructor. */ 24 | 25 | 26 | /// The build() method. 27 | /// 28 | /// @param inContext The BuildContext for this widget. 29 | /// @return A Widget. 30 | Widget build(BuildContext inContext) { 31 | 32 | print("## Tasks.build()"); 33 | 34 | return ScopedModel( 35 | model : tasksModel, 36 | child : ScopedModelDescendant( 37 | builder : (BuildContext inContext, Widget inChild, TasksModel inModel) { 38 | return IndexedStack( 39 | index : inModel.stackIndex, 40 | children : [ 41 | TasksList(), 42 | TasksEntry() 43 | ] /* End IndexedStack children. */ 44 | ); /* End IndexedStack. */ 45 | } /* End ScopedModelDescendant builder(). */ 46 | ) /* End ScopedModelDescendant. */ 47 | ); /* End ScopedModel. */ 48 | 49 | } /* End build(). */ 50 | 51 | 52 | } /* End class. */ 53 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/lib/tasks/TasksModel.dart: -------------------------------------------------------------------------------- 1 | import "../BaseModel.dart"; 2 | 3 | 4 | /// A class representing this PIM entity type. 5 | class Task { 6 | 7 | 8 | /// The fields this entity type contains. 9 | int id; 10 | String description; 11 | String dueDate; // YYYY,MM,DD 12 | String completed = "false"; 13 | 14 | 15 | /// Just for debugging, so we get something useful in the console. 16 | String toString() { 17 | return "{ id=$id, description=$description, dueDate=$dueDate, completed=$completed }"; 18 | } 19 | 20 | 21 | } /* End class. */ 22 | 23 | 24 | /// ******************************************************************************************************************** 25 | /// The model backing this entity type's views. 26 | /// ******************************************************************************************************************** 27 | class TasksModel extends BaseModel { 28 | } /* End class. */ 29 | 30 | 31 | // The one and only instance of this model. 32 | TasksModel tasksModel = TasksModel(); 33 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/lib/utils.dart: -------------------------------------------------------------------------------- 1 | /// ******************************************************************************************************************** 2 | /// A few global utility-type things needed by multiple places in the codebase. 3 | /// ******************************************************************************************************************** 4 | 5 | 6 | import "dart:io"; 7 | import "package:flutter/material.dart"; 8 | import "package:intl/intl.dart"; 9 | import "BaseModel.dart"; 10 | 11 | 12 | /// The application's document directory for contact avatar image files and database files. 13 | Directory docsDir; 14 | 15 | 16 | /// Function for getting a selected date from the user. 17 | /// 18 | /// @param inContext The BuildContext of the parent Widget. 19 | /// @return Future. 20 | Future selectDate(BuildContext inContext, BaseModel inModel, String inDateString) async { 21 | 22 | print("## globals.selectDate()"); 23 | 24 | // Default to today's date, assuming we're adding. 25 | DateTime initialDate = DateTime.now(); 26 | 27 | // If editing, set the initialDate to the current birthday, if any. 28 | if (inDateString != null) { 29 | List dateParts = inDateString.split(","); 30 | // Create a DateTime using the year, month and day from dateParts. 31 | initialDate = DateTime(int.parse(dateParts[0]), int.parse(dateParts[1]), int.parse(dateParts[2])); 32 | } 33 | 34 | // Now request the date. 35 | DateTime picked = await showDatePicker( 36 | context : inContext, 37 | initialDate : initialDate, 38 | firstDate : DateTime(1900), 39 | lastDate : DateTime(2100) 40 | ); 41 | 42 | // If they didn't cancel, update it in the model so it shows on the screen and return the string form. 43 | if (picked != null) { 44 | inModel.setChosenDate(DateFormat.yMMMMd("en_US").format(picked.toLocal())); 45 | return "${picked.year},${picked.month},${picked.day}"; 46 | } 47 | 48 | } /* End _selectDate(). */ -------------------------------------------------------------------------------- /ch_05+06/flutter_book/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_book 2 | description: flutter_book 3 | 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # Read more about versioning at semver.org. 10 | version: 1.0.0+1 11 | 12 | environment: 13 | sdk: ">=2.1.0 <3.0.0" 14 | 15 | dependencies: 16 | flutter: 17 | sdk: flutter 18 | scoped_model: 1.0.1 19 | sqflite: 1.2.0 20 | path_provider: 1.6.0 21 | flutter_slidable: 0.5.4 22 | intl: 0.16.1 23 | image_picker: 0.6.3+1 24 | flutter_calendar_carousel: 1.4.10 25 | 26 | # The following adds the Cupertino Icons font to your application. 27 | # Use with the CupertinoIcons class for iOS style icons. 28 | cupertino_icons: 0.1.3 29 | 30 | dev_dependencies: 31 | flutter_test: 32 | sdk: flutter 33 | 34 | 35 | # For information on the generic Dart part of this file, see the 36 | # following page: https://www.dartlang.org/tools/pub/pubspec 37 | 38 | # The following section is specific to Flutter. 39 | flutter: 40 | 41 | # The following line ensures that the Material Icons font is 42 | # included with your application, so that you can use the icons in 43 | # the material Icons class. 44 | uses-material-design: true 45 | 46 | # assets: 47 | # - assets/ 48 | 49 | # An image asset can refer to one or more resolution-specific "variants", see 50 | # https://flutter.io/assets-and-images/#resolution-aware. 51 | 52 | # For details regarding adding assets from package dependencies, see 53 | # https://flutter.io/assets-and-images/#from-packages 54 | 55 | # To add custom fonts to your application, add a fonts section here, 56 | # in this "flutter" section. Each entry in this list should have a 57 | # "family" key with the font family name, and a "fonts" key with a 58 | # list giving the asset and other descriptors for the font. For 59 | # example: 60 | # fonts: 61 | # - family: Schyler 62 | # fonts: 63 | # - asset: fonts/Schyler-Regular.ttf 64 | # - asset: fonts/Schyler-Italic.ttf 65 | # style: italic 66 | # - family: Trajan Pro 67 | # fonts: 68 | # - asset: fonts/TrajanPro.ttf 69 | # - asset: fonts/TrajanPro_Bold.ttf 70 | # weight: 700 71 | # 72 | # For details regarding fonts from package dependencies, 73 | # see https://flutter.io/custom-fonts/#from-packages 74 | -------------------------------------------------------------------------------- /ch_05+06/flutter_book/res/values/strings_en.arb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_05+06/flutter_book/res/values/strings_en.arb -------------------------------------------------------------------------------- /ch_05+06/flutter_book/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:flutter_book/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(FlutterBook()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/.flutter-plugins-dependencies: -------------------------------------------------------------------------------- 1 | {"_info":"// This is a generated file; do not edit or check into version control.","dependencyGraph":[{"name":"flutter_socket_io","dependencies":[]},{"name":"path_provider","dependencies":[]}]} -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.lock 4 | *.log 5 | *.pyc 6 | *.swp 7 | .DS_Store 8 | .atom/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # Visual Studio Code related 20 | .vscode/ 21 | 22 | # Flutter/Dart/Pub related 23 | **/doc/api/ 24 | .dart_tool/ 25 | .flutter-plugins 26 | .packages 27 | .pub-cache/ 28 | .pub/ 29 | build/ 30 | 31 | # Android related 32 | **/android/**/gradle-wrapper.jar 33 | **/android/.gradle 34 | **/android/captures/ 35 | **/android/gradlew 36 | **/android/gradlew.bat 37 | **/android/local.properties 38 | **/android/**/GeneratedPluginRegistrant.java 39 | 40 | # iOS/XCode related 41 | **/ios/**/*.mode1v3 42 | **/ios/**/*.mode2v3 43 | **/ios/**/*.moved-aside 44 | **/ios/**/*.pbxuser 45 | **/ios/**/*.perspectivev3 46 | **/ios/**/*sync/ 47 | **/ios/**/.sconsign.dblite 48 | **/ios/**/.tags* 49 | **/ios/**/.vagrant/ 50 | **/ios/**/DerivedData/ 51 | **/ios/**/Icon? 52 | **/ios/**/Pods/ 53 | **/ios/**/.symlinks/ 54 | **/ios/**/profile 55 | **/ios/**/xcuserdata 56 | **/ios/.generated/ 57 | **/ios/Flutter/App.framework 58 | **/ios/Flutter/Flutter.framework 59 | **/ios/Flutter/Generated.xcconfig 60 | **/ios/Flutter/app.flx 61 | **/ios/Flutter/app.zip 62 | **/ios/Flutter/flutter_assets/ 63 | **/ios/ServiceDefinitions.json 64 | **/ios/Runner/GeneratedPluginRegistrant.* 65 | 66 | # Exceptions to above rules. 67 | !**/ios/**/default.mode1v3 68 | !**/ios/**/default.mode2v3 69 | !**/ios/**/default.pbxuser 70 | !**/ios/**/default.perspectivev3 71 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 72 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 5391447fae6209bb21a89e6a5a6583cac1af9b4b 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/README.md: -------------------------------------------------------------------------------- 1 | # flutter_chat 2 | 3 | flutter_chat 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.io/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.io/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.io/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | android { 28 | compileSdkVersion 28 29 | 30 | lintOptions { 31 | disable 'InvalidPackage' 32 | } 33 | 34 | defaultConfig { 35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 36 | applicationId "com.apress.flutterchat" 37 | minSdkVersion 16 38 | targetSdkVersion 28 39 | versionCode flutterVersionCode.toInteger() 40 | versionName flutterVersionName 41 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 42 | } 43 | 44 | buildTypes { 45 | release { 46 | // TODO: Add your own signing config for the release build. 47 | // Signing with the debug keys for now, so `flutter run --release` works. 48 | signingConfig signingConfigs.debug 49 | } 50 | } 51 | } 52 | 53 | flutter { 54 | source '../..' 55 | } 56 | 57 | dependencies { 58 | testImplementation 'junit:junit:4.12' 59 | androidTestImplementation 'androidx.test:runner:1.2.0' 60 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 61 | } 62 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 15 | 20 | 27 | 31 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/android/app/src/main/java/com/apress/flutterchat/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.apress.flutterchat; 2 | 3 | import android.os.Bundle; 4 | import io.flutter.app.FlutterActivity; 5 | import io.flutter.plugins.GeneratedPluginRegistrant; 6 | 7 | public class MainActivity extends FlutterActivity { 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | GeneratedPluginRegistrant.registerWith(this); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.3.0' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=false 3 | android.useAndroidX=true 4 | android.enableJetifier=true -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip 7 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/android/settings_aar.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/assets/drawback01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/assets/drawback01.jpg -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/assets/private.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/assets/private.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/assets/public.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/assets/public.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/assets/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/assets/user.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Flutter/flutter_export_environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is a generated file; do not edit or check into version control. 3 | export "FLUTTER_ROOT=C:\library\flutter" 4 | export "FLUTTER_APPLICATION_PATH=C:\temp\practical-flutter\ch_07+08\flutter_chat" 5 | export "FLUTTER_TARGET=lib\main.dart" 6 | export "FLUTTER_BUILD_DIR=build" 7 | export "SYMROOT=${SOURCE_ROOT}/../build\ios" 8 | export "FLUTTER_FRAMEWORK_DIR=C:\library\flutter\bin\cache\artifacts\engine\ios" 9 | export "FLUTTER_BUILD_NAME=1.0.0" 10 | export "FLUTTER_BUILD_NUMBER=1" 11 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | flutter_chat 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/lib/AppDrawer.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | import "package:scoped_model/scoped_model.dart"; 3 | import "Model.dart" show FlutterChatModel, model; 4 | import "Connector.dart" as connector; 5 | 6 | 7 | class AppDrawer extends StatelessWidget { 8 | 9 | 10 | /// The build() method. 11 | /// 12 | /// @param inContext The BuildContext for this widget. 13 | /// @return A Widget. 14 | Widget build(final BuildContext inContext) { 15 | 16 | print("## AppDrawer.build()"); 17 | 18 | return ScopedModel(model : model, child : ScopedModelDescendant( 19 | builder : (BuildContext inContext, Widget inChild, FlutterChatModel inModel) { 20 | return Drawer( 21 | child : Column(children : [ 22 | // Header. 23 | Container( 24 | decoration : BoxDecoration(image : DecorationImage( 25 | image : AssetImage("assets/drawback01.jpg"), fit : BoxFit.cover 26 | )), 27 | child : Padding(padding : EdgeInsets.fromLTRB(0, 30, 0, 15), 28 | child : ListTile( 29 | title : Padding(padding : EdgeInsets.fromLTRB(0, 0, 0, 20), 30 | child : Center(child : Text(model.userName, 31 | style : TextStyle(color : Colors.white, fontSize : 24) 32 | )) 33 | ), 34 | subtitle : Center(child : Text(model.currentRoomName, 35 | style : TextStyle(color : Colors.white, fontSize : 16) 36 | )) 37 | ) 38 | ) 39 | ), 40 | // Lobby (room list). 41 | Padding(padding : EdgeInsets.fromLTRB(0, 20, 0, 0), 42 | child : ListTile( 43 | leading : Icon(Icons.list), 44 | title : Text("Lobby"), 45 | onTap: () { 46 | // Navigate to new screen, ensuring all others except Home are removed from navigation. 47 | Navigator.of(inContext).pushNamedAndRemoveUntil("/Lobby", ModalRoute.withName("/")); 48 | // Call server to get room list. 49 | connector.listRooms((inRoomList) { 50 | print("## AppDrawer.listRooms: callback: inRoomList=$inRoomList"); 51 | // Update the model with the new list of rooms. 52 | model.setRoomList(inRoomList); 53 | }); 54 | } 55 | ) 56 | ), 57 | // Current Room. 58 | ListTile( 59 | enabled : model.currentRoomEnabled, 60 | leading : Icon(Icons.forum), 61 | title : Text("Current Room"), 62 | onTap: () { 63 | // Navigate to new screen, ensuring all others except Home are removed from navigation. 64 | Navigator.of(inContext).pushNamedAndRemoveUntil("/Room", ModalRoute.withName("/")); 65 | } 66 | ), 67 | // User List. 68 | ListTile( 69 | leading : Icon(Icons.face), 70 | title : Text("User List"), 71 | onTap: () { 72 | // Navigate to new screen, ensuring all others except Home are removed from navigation. 73 | Navigator.of(inContext).pushNamedAndRemoveUntil("/UserList", ModalRoute.withName("/")); 74 | // Call server to get user list. 75 | connector.listUsers((inUserList) { 76 | print("## AppDrawer.listUsers: callback: inUserList=$inUserList"); 77 | // Update the model with the new list of users. 78 | model.setUserList(inUserList); 79 | }); 80 | } 81 | ) 82 | ]) /* End Column/Column.children. */ 83 | ); /* End Drawer. */ 84 | } /* End ScopedModel.builder(). */ 85 | )); /* End ScopedModel/ScopedModelDescendant. */ 86 | 87 | } /* End build(). */ 88 | 89 | 90 | } /* End class. */ -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/lib/Home.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | import "package:scoped_model/scoped_model.dart"; 3 | import "Model.dart" show FlutterChatModel, model; 4 | import "AppDrawer.dart"; 5 | 6 | 7 | class Home extends StatelessWidget { 8 | 9 | 10 | /// The build() method. 11 | /// 12 | /// @param inContext The BuildContext for this widget. 13 | /// @return A Widget. 14 | Widget build(final BuildContext inContext) { 15 | 16 | print("## Home.build()"); 17 | 18 | return ScopedModel(model : model, child : ScopedModelDescendant( 19 | builder : (BuildContext inContext, Widget inChild, FlutterChatModel inModel) { 20 | return Scaffold( 21 | appBar : AppBar(title : Text("FlutterChat")), 22 | drawer : AppDrawer(), 23 | body : Center(child : Text(model.greeting)) 24 | ); 25 | } 26 | )); 27 | 28 | } /* End build(). */ 29 | 30 | 31 | } /* End class. */ -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/lib/UserList.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | import "package:scoped_model/scoped_model.dart"; 3 | import "Model.dart" show FlutterChatModel, model; 4 | import "AppDrawer.dart"; 5 | 6 | 7 | class UserList extends StatelessWidget { 8 | 9 | 10 | /// The build() method. 11 | /// 12 | /// @param inContext The BuildContext for this widget. 13 | /// @return A Widget. 14 | Widget build(final BuildContext inContext) { 15 | 16 | print("## UserList.build()"); 17 | 18 | return ScopedModel(model : model, child : ScopedModelDescendant( 19 | builder : (BuildContext inContext, Widget inChild, FlutterChatModel inModel) { 20 | return Scaffold( 21 | appBar : AppBar(title : Text("User List")), 22 | drawer : AppDrawer(), 23 | body : GridView.builder( 24 | itemCount: model.userList.length, 25 | gridDelegate : SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount : 3), 26 | itemBuilder : (BuildContext inContext, int inIndex) { 27 | Map user = model.userList[inIndex]; 28 | return Padding(padding : EdgeInsets.fromLTRB(10, 10, 10, 10), child : Card( 29 | child : Padding(padding : EdgeInsets.fromLTRB(10, 10, 10, 10), 30 | child : GridTile( 31 | child : Center(child : Padding(padding : EdgeInsets.fromLTRB(0, 0, 0, 20), 32 | child : Image.asset("assets/user.png") 33 | )), 34 | footer : Text(user["userName"], textAlign : TextAlign.center) 35 | ) 36 | ) 37 | )); 38 | } 39 | ) 40 | ); 41 | } 42 | )); 43 | 44 | } /* End build(). */ 45 | 46 | 47 | } /* End class. */ -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/lib/generated/i18n.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | // ignore_for_file: non_constant_identifier_names 7 | // ignore_for_file: camel_case_types 8 | // ignore_for_file: prefer_single_quotes 9 | 10 | // This file is automatically generated. DO NOT EDIT, all your changes would be lost. 11 | class S implements WidgetsLocalizations { 12 | const S(); 13 | 14 | static S current; 15 | 16 | static const GeneratedLocalizationsDelegate delegate = 17 | GeneratedLocalizationsDelegate(); 18 | 19 | static S of(BuildContext context) => Localizations.of(context, S); 20 | 21 | @override 22 | TextDirection get textDirection => TextDirection.ltr; 23 | 24 | } 25 | 26 | class $en extends S { 27 | const $en(); 28 | } 29 | 30 | class GeneratedLocalizationsDelegate extends LocalizationsDelegate { 31 | const GeneratedLocalizationsDelegate(); 32 | 33 | List get supportedLocales { 34 | return const [ 35 | Locale("en", ""), 36 | ]; 37 | } 38 | 39 | LocaleListResolutionCallback listResolution({Locale fallback, bool withCountry = true}) { 40 | return (List locales, Iterable supported) { 41 | if (locales == null || locales.isEmpty) { 42 | return fallback ?? supported.first; 43 | } else { 44 | return _resolve(locales.first, fallback, supported, withCountry); 45 | } 46 | }; 47 | } 48 | 49 | LocaleResolutionCallback resolution({Locale fallback, bool withCountry = true}) { 50 | return (Locale locale, Iterable supported) { 51 | return _resolve(locale, fallback, supported, withCountry); 52 | }; 53 | } 54 | 55 | @override 56 | Future load(Locale locale) { 57 | final String lang = getLang(locale); 58 | if (lang != null) { 59 | switch (lang) { 60 | case "en": 61 | S.current = const $en(); 62 | return SynchronousFuture(S.current); 63 | default: 64 | // NO-OP. 65 | } 66 | } 67 | S.current = const S(); 68 | return SynchronousFuture(S.current); 69 | } 70 | 71 | @override 72 | bool isSupported(Locale locale) => _isSupported(locale, true); 73 | 74 | @override 75 | bool shouldReload(GeneratedLocalizationsDelegate old) => false; 76 | 77 | /// 78 | /// Internal method to resolve a locale from a list of locales. 79 | /// 80 | Locale _resolve(Locale locale, Locale fallback, Iterable supported, bool withCountry) { 81 | if (locale == null || !_isSupported(locale, withCountry)) { 82 | return fallback ?? supported.first; 83 | } 84 | 85 | final Locale languageLocale = Locale(locale.languageCode, ""); 86 | if (supported.contains(locale)) { 87 | return locale; 88 | } else if (supported.contains(languageLocale)) { 89 | return languageLocale; 90 | } else { 91 | final Locale fallbackLocale = fallback ?? supported.first; 92 | return fallbackLocale; 93 | } 94 | } 95 | 96 | /// 97 | /// Returns true if the specified locale is supported, false otherwise. 98 | /// 99 | bool _isSupported(Locale locale, bool withCountry) { 100 | if (locale != null) { 101 | for (Locale supportedLocale in supportedLocales) { 102 | // Language must always match both locales. 103 | if (supportedLocale.languageCode != locale.languageCode) { 104 | continue; 105 | } 106 | 107 | // If country code matches, return this locale. 108 | if (supportedLocale.countryCode == locale.countryCode) { 109 | return true; 110 | } 111 | 112 | // If no country requirement is requested, check if this locale has no country. 113 | if (true != withCountry && (supportedLocale.countryCode == null || supportedLocale.countryCode.isEmpty)) { 114 | return true; 115 | } 116 | } 117 | } 118 | return false; 119 | } 120 | } 121 | 122 | String getLang(Locale l) => l == null 123 | ? null 124 | : l.countryCode != null && l.countryCode.isEmpty 125 | ? l.languageCode 126 | : l.toString(); 127 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_chat 2 | description: flutter_chat 3 | 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # Read more about versioning at semver.org. 10 | version: 1.0.0+1 11 | 12 | environment: 13 | sdk: ">=2.1.0 <3.0.0" 14 | 15 | dependencies: 16 | flutter: 17 | sdk: flutter 18 | flutter_socket_io: 0.6.0 19 | path_provider: 1.6.0 20 | scoped_model: 1.0.1 21 | 22 | # The following adds the Cupertino Icons font to your application. 23 | # Use with the CupertinoIcons class for iOS style icons. 24 | cupertino_icons: ^0.1.2 25 | 26 | dev_dependencies: 27 | flutter_test: 28 | sdk: flutter 29 | 30 | 31 | # For information on the generic Dart part of this file, see the 32 | # following page: https://www.dartlang.org/tools/pub/pubspec 33 | 34 | # The following section is specific to Flutter. 35 | flutter: 36 | 37 | # The following line ensures that the Material Icons font is 38 | # included with your application, so that you can use the icons in 39 | # the material Icons class. 40 | uses-material-design: true 41 | 42 | # To add assets to your application, add an assets section, like this: 43 | assets: 44 | - assets/ 45 | 46 | # An image asset can refer to one or more resolution-specific "variants", see 47 | # https://flutter.io/assets-and-images/#resolution-aware. 48 | 49 | # For details regarding adding assets from package dependencies, see 50 | # https://flutter.io/assets-and-images/#from-packages 51 | 52 | # To add custom fonts to your application, add a fonts section here, 53 | # in this "flutter" section. Each entry in this list should have a 54 | # "family" key with the font family name, and a "fonts" key with a 55 | # list giving the asset and other descriptors for the font. For 56 | # example: 57 | # fonts: 58 | # - family: Schyler 59 | # fonts: 60 | # - asset: fonts/Schyler-Regular.ttf 61 | # - asset: fonts/Schyler-Italic.ttf 62 | # style: italic 63 | # - family: Trajan Pro 64 | # fonts: 65 | # - asset: fonts/TrajanPro.ttf 66 | # - asset: fonts/TrajanPro_Bold.ttf 67 | # weight: 700 68 | # 69 | # For details regarding fonts from package dependencies, 70 | # see https://flutter.io/custom-fonts/#from-packages 71 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/res/values/strings_en.arb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_07+08/flutter_chat/res/values/strings_en.arb -------------------------------------------------------------------------------- /ch_07+08/flutter_chat/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:flutter_chat/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(FlutterChat()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat_server/nm.bat: -------------------------------------------------------------------------------- 1 | nodemon --no-colors 2 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat_server/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "verbose": true, 3 | "noColors": true, 4 | "events": { 5 | "start": "cls || clear" 6 | } 7 | } -------------------------------------------------------------------------------- /ch_07+08/flutter_chat_server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flutter_chat_server", 3 | "version": "1.0.0", 4 | "description": "The server side of FlutterChat", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Frank W. Zammetti", 10 | "license": "ISC", 11 | "dependencies": { 12 | "socket.io": "2.2.0" 13 | }, 14 | "devDependencies": { 15 | "eslint": "^5.15.3" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ch_07+08/flutter_chat_server/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tester for FlutterChat server 5 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | 31 | 32 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # Visual Studio Code related 19 | .vscode/ 20 | 21 | # Flutter/Dart/Pub related 22 | **/doc/api/ 23 | .dart_tool/ 24 | .flutter-plugins 25 | .packages 26 | .pub-cache/ 27 | .pub/ 28 | /build/ 29 | 30 | # Android related 31 | **/android/**/gradle-wrapper.jar 32 | **/android/.gradle 33 | **/android/captures/ 34 | **/android/gradlew 35 | **/android/gradlew.bat 36 | **/android/local.properties 37 | **/android/**/GeneratedPluginRegistrant.java 38 | 39 | # iOS/XCode related 40 | **/ios/**/*.mode1v3 41 | **/ios/**/*.mode2v3 42 | **/ios/**/*.moved-aside 43 | **/ios/**/*.pbxuser 44 | **/ios/**/*.perspectivev3 45 | **/ios/**/*sync/ 46 | **/ios/**/.sconsign.dblite 47 | **/ios/**/.tags* 48 | **/ios/**/.vagrant/ 49 | **/ios/**/DerivedData/ 50 | **/ios/**/Icon? 51 | **/ios/**/Pods/ 52 | **/ios/**/.symlinks/ 53 | **/ios/**/profile 54 | **/ios/**/xcuserdata 55 | **/ios/.generated/ 56 | **/ios/Flutter/App.framework 57 | **/ios/Flutter/Flutter.framework 58 | **/ios/Flutter/Generated.xcconfig 59 | **/ios/Flutter/app.flx 60 | **/ios/Flutter/app.zip 61 | **/ios/Flutter/flutter_assets/ 62 | **/ios/ServiceDefinitions.json 63 | **/ios/Runner/GeneratedPluginRegistrant.* 64 | 65 | # Exceptions to above rules. 66 | !**/ios/**/default.mode1v3 67 | !**/ios/**/default.mode2v3 68 | !**/ios/**/default.pbxuser 69 | !**/ios/**/default.perspectivev3 70 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 71 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 8661d8aecd626f7f57ccbcb735553edc05a2e713 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/README.md: -------------------------------------------------------------------------------- 1 | # flutter_hero 2 | 3 | FlutterHero 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.io/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.io/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.io/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | android { 28 | compileSdkVersion 28 29 | 30 | lintOptions { 31 | disable 'InvalidPackage' 32 | } 33 | 34 | defaultConfig { 35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 36 | applicationId "com.apress.flutter_hero" 37 | minSdkVersion 16 38 | targetSdkVersion 28 39 | versionCode flutterVersionCode.toInteger() 40 | versionName flutterVersionName 41 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 42 | } 43 | 44 | buildTypes { 45 | release { 46 | // TODO: Add your own signing config for the release build. 47 | // Signing with the debug keys for now, so `flutter run --release` works. 48 | signingConfig signingConfigs.debug 49 | } 50 | } 51 | } 52 | 53 | flutter { 54 | source '../..' 55 | } 56 | 57 | dependencies { 58 | testImplementation 'junit:junit:4.12' 59 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 60 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 61 | } 62 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 13 | 20 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/android/app/src/main/java/com/apress/flutter_hero/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.apress.flutter_hero; 2 | 3 | import android.os.Bundle; 4 | import io.flutter.app.FlutterActivity; 5 | import io.flutter.plugins.GeneratedPluginRegistrant; 6 | 7 | public class MainActivity extends FlutterActivity { 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | GeneratedPluginRegistrant.registerWith(this); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.2.1' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip 7 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/alien-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/alien-0.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/alien-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/alien-1.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/asteroid-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/asteroid-0.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/asteroid-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/asteroid-1.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/background.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/crystal-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/crystal-0.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/crystal-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/crystal-1.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/crystal-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/crystal-2.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/crystal-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/crystal-3.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/delivery.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/delivery.mp3 -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/explosion-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/explosion-0.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/explosion-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/explosion-1.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/explosion-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/explosion-2.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/explosion-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/explosion-3.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/explosion-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/explosion-4.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/explosion.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/explosion.mp3 -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/fill.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/fill.mp3 -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/fish-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/fish-0.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/fish-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/fish-1.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/planet-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/planet-0.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/player-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/player-0.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/player-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/player-1.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/robot-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/robot-0.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/robot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/robot-1.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/assets/thrust.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/assets/thrust.mp3 -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | flutter_hero 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/lib/Enemy.dart: -------------------------------------------------------------------------------- 1 | import "GameObject.dart"; 2 | 3 | 4 | /// A type of GameObject specific to the four types of enemies. Needed because enemies can move. 5 | class Enemy extends GameObject { 6 | 7 | 8 | /// The speed this object moves per game loop tick in pixels. 9 | int speed = 0; 10 | 11 | /// What direction this enemy is moving in (0=left, 1 =right); 12 | int moveDirection = 0; 13 | 14 | 15 | /// Constructor. 16 | Enemy(double inScreenWidth, double inScreenHeight, String inBaseFilename, 17 | int inWidth, int inHeight, int inNumFrames, int inFrameSkip, 18 | int inMoveDirection, int inSpeed 19 | ) : super(inScreenWidth, inScreenHeight, inBaseFilename, inWidth, inHeight, inNumFrames, inFrameSkip, null) { 20 | speed = inSpeed; 21 | moveDirection = inMoveDirection; 22 | } 23 | 24 | 25 | /// Move this enemy. 26 | void move() { 27 | 28 | if (moveDirection == 1) { 29 | x = x + speed; 30 | if (x > screenWidth + width) { 31 | x = -width.toDouble(); 32 | } 33 | } else { 34 | x = x - speed; 35 | if (x < -width) { 36 | x = screenWidth + width.toDouble(); 37 | } 38 | } 39 | 40 | } /* End move(). */ 41 | 42 | 43 | } /* End class. */ 44 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/lib/GameObject.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | 4 | /// Base class for all objects in the game. This contains the code common to enemies, the player, the crystal 5 | /// and the planet. 6 | class GameObject { 7 | 8 | 9 | /// The width of the screen. 10 | double screenWidth = 0.0; 11 | 12 | /// The height of the screen. 13 | double screenHeight = 0.0; 14 | 15 | /// Base of the asset files (portion before -X.png). 16 | String baseFilename = ""; 17 | 18 | /// The width of the image assets. 19 | int width = 0; 20 | 21 | /// The height to the image assets. 22 | int height = 0; 23 | 24 | /// The X location of this object. 25 | double x = 0.0; 26 | 27 | /// The Y location of this object. 28 | double y = 0.0; 29 | 30 | /// The number of frames in this object's animation sequence. 31 | int numFrames = 0; 32 | 33 | /// How many game loop ticks occur between animation frame changes. 34 | int frameSkip = 0; 35 | 36 | /// The function to call when the animation sequence completes. Needed to accommodate explosions. 37 | Function animationCallback; 38 | 39 | /// The current animation frame showing. 40 | int currentFrame = 0; 41 | 42 | /// Counter that counts up to frameSkip and then increments currentFrame. 43 | int frameCount = 0; 44 | 45 | /// The List of Image assets (frames of the animation sequence for this object). 46 | List frames = [ ]; 47 | 48 | /// Is this object visible or not? 49 | bool visible = true; 50 | 51 | 52 | /// Constructor. 53 | /// 54 | /// @param inScreenWidth Value for screenWidth. 55 | /// @param inScreenHeight Value for screenHeight. 56 | /// @param inBaseFilename Value for baseFilename. 57 | /// @param inWidth Value for width. 58 | /// @param inHeight Value for height. 59 | /// @param inNumFrames Value for numFrames. 60 | /// @param inFrameSkip Value for frameSkip. 61 | /// @param inAnimationCallback The function to call when the animation sequence completes. 62 | GameObject(double inScreenWidth, double inScreenHeight, String inBaseFilename, 63 | int inWidth, int inHeight, 64 | int inNumFrames, int inFrameSkip, Function inAnimationCallback 65 | ) { 66 | 67 | screenWidth = inScreenWidth; 68 | screenHeight = inScreenHeight; 69 | baseFilename = inBaseFilename; 70 | width = inWidth; 71 | height = inHeight; 72 | numFrames = inNumFrames; 73 | frameSkip = inFrameSkip; 74 | animationCallback = inAnimationCallback; 75 | 76 | // Load all the animation sequence frames. 77 | for (int i = 0; i < inNumFrames; i++) { 78 | frames.add(Image.asset("assets/$baseFilename-$i.png")); 79 | } 80 | 81 | } /* End constructor. */ 82 | 83 | 84 | /// Give this game object the opportunity to animate. Called once per game loop tick. 85 | void animate() { 86 | 87 | frameCount = frameCount + 1; 88 | if (frameCount > frameSkip) { 89 | frameCount = 0; 90 | currentFrame = currentFrame + 1; 91 | if (currentFrame == numFrames) { 92 | currentFrame = 0; 93 | if (animationCallback != null) { 94 | animationCallback(); 95 | } 96 | } 97 | } 98 | 99 | } /* End animate(). */ 100 | 101 | 102 | /// Returns a widget that is the visual representation of this object. 103 | Widget draw() { 104 | 105 | return visible ? 106 | Positioned(left : x, top : y, child : frames[currentFrame]) : 107 | Positioned(child : Container()); 108 | 109 | } /* End draw(). */ 110 | 111 | 112 | } /* End class. */ 113 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/lib/InputController.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | import "Player.dart"; 3 | 4 | 5 | /// The touch "anchor point", that is, the coordinates of the initial touch. 6 | double touchAnchorX; 7 | double touchAnchorY; 8 | 9 | /// How many pixels from the anchor point the player has to move their finger to trigger movement. 10 | int moveSensitivity = 20; 11 | 12 | /// Reference the player object. 13 | Player player; 14 | 15 | 16 | /// init. 17 | /// 18 | /// @param inPlayer Reference to the player object. 19 | void init(Player inPlayer) { 20 | 21 | player = inPlayer; 22 | 23 | } /* End init(). */ 24 | 25 | 26 | /// Triggered when the player places their finger on the screen. Capture the anchor point here. 27 | /// 28 | /// @param inDetails The DragStartDetails object describing the gesture. 29 | void onPanStart(DragStartDetails inDetails) { 30 | 31 | touchAnchorX = inDetails.globalPosition.dx; 32 | touchAnchorY = inDetails.globalPosition.dy; 33 | player.moveHorizontal = 0; 34 | player.moveVertical = 0; 35 | 36 | } /* End onPanStart(). */ 37 | 38 | 39 | /// Triggered when the player moves their finger around the screen. 40 | /// 41 | /// @param inDetails The DragUpdateDetails object describing the gesture. 42 | void onPanUpdate(DragUpdateDetails inDetails) { 43 | 44 | // Left. 45 | if (inDetails.globalPosition.dx < touchAnchorX - moveSensitivity) { 46 | player.moveHorizontal = -1; 47 | player.orientationChanged(); 48 | // Right. 49 | } else if (inDetails.globalPosition.dx > touchAnchorX + moveSensitivity) { 50 | player.moveHorizontal = 1; 51 | player.orientationChanged(); 52 | // Not far enough to trigger horizontal movement. 53 | } else { 54 | player.moveHorizontal = 0; 55 | player.orientationChanged(); 56 | } 57 | // Up. 58 | if (inDetails.globalPosition.dy < touchAnchorY - moveSensitivity) { 59 | player.moveVertical = -1; 60 | player.orientationChanged(); 61 | // Down. 62 | } else if (inDetails.globalPosition.dy > touchAnchorY + moveSensitivity) { 63 | player.moveVertical = 1; 64 | player.orientationChanged(); 65 | // Not far enough to trigger vertical movement. 66 | } else { 67 | player.moveVertical = 0; 68 | player.orientationChanged(); 69 | } 70 | 71 | } /* End onPanUpdate(). */ 72 | 73 | 74 | /// Triggered when the player lifts their finger from the screen. 75 | /// 76 | /// @param inDetails The object describing the gesture. Note this can be multiple types, hence dynamic. 77 | void onPanEnd(dynamic inDetails) { 78 | 79 | player.moveHorizontal = 0; 80 | player.moveVertical = 0; 81 | 82 | } /* End onPanEnd(). */ 83 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/lib/Player.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | import "GameObject.dart"; 3 | 4 | 5 | /// A type of GameObject specific to the player. Needed because the player can of course can move. 6 | class Player extends GameObject { 7 | 8 | 9 | /// The speed this object moves per game loop tick in pixels. 10 | int speed = 0; 11 | 12 | /// Which way the player is moving along the horizontal axis, if at all. 0=not moving, 1=right, -1=left. 13 | int moveHorizontal = 0; 14 | 15 | /// Which way the player is moving along the vertical axis, if at all. 0=not moving, 1=down, -1=up. 16 | int moveVertical = 0; 17 | 18 | /// The amount of energy currently onboard the ship. 19 | double energy = 0.0; 20 | 21 | /// A precalculated table of angles to radians, to avoid calculations and local vars. 22 | Map anglesToRadiansConversionTable = { 23 | "angle45" : 0.7853981633974483, 24 | "angle90" : 1.5707963267948966, 25 | "angle135" : 2.3387411976724017, 26 | "angle180" : 3.141592653589793, 27 | "angle225" : 3.9269908169872414, 28 | "angle270" : 4.71238898038469, 29 | "angle315" : 5.497787143782138 30 | }; 31 | 32 | /// The number of radians the player is currently rotated by. 33 | double radians = 0.0; 34 | 35 | 36 | /// Constructor. 37 | Player(double inScreenWidth, double inScreenHeight, String inBaseFilename, 38 | int inWidth, int inHeight, 39 | int inNumFrames, int inFrameSkip, int inSpeed, 40 | ) : super(inScreenWidth, inScreenHeight, inBaseFilename, inWidth, inHeight, inNumFrames, inFrameSkip, null) { 41 | speed = inSpeed; 42 | } 43 | 44 | 45 | /// Returns a widget that is the visual representation of this object. For the player, we need to rotate 46 | /// as appropriate for movement, hence the override of draw() in GameObject. 47 | @override 48 | Widget draw() { 49 | 50 | return visible ? 51 | Positioned(left : x, top : y, child : Transform.rotate(angle : radians, child : frames[currentFrame])) : 52 | Positioned(child : Container()); 53 | 54 | } /* End draw(). */ 55 | 56 | 57 | /// Move the player. 58 | void move() { 59 | 60 | if (x > 0 && moveHorizontal == -1) { 61 | x = x - speed; 62 | } 63 | if (x < (screenWidth - width) && moveHorizontal == 1) { 64 | x = x + speed; 65 | } 66 | if (y > 40 && moveVertical == -1) { 67 | y = y - speed; 68 | } 69 | if (y < (screenHeight - height - 10) && moveVertical == 1) { 70 | y = y + speed; 71 | } 72 | 73 | } /* End move(). */ 74 | 75 | 76 | /// Called whenever the player's orientation changes. 77 | void orientationChanged() { 78 | 79 | radians = 0.0; 80 | if (moveHorizontal == 1 && moveVertical == -1) { 81 | radians = anglesToRadiansConversionTable["angle45"]; 82 | } else if (moveHorizontal == 1 && moveVertical == 0) { 83 | radians = anglesToRadiansConversionTable["angle90"]; 84 | } else if (moveHorizontal == 1 && moveVertical == 1) { 85 | radians = anglesToRadiansConversionTable["angle135"]; 86 | } else if (moveHorizontal == 0 && moveVertical == 1) { 87 | radians = anglesToRadiansConversionTable["angle180"]; 88 | } else if (moveHorizontal == -1 && moveVertical == 1) { 89 | radians = anglesToRadiansConversionTable["angle225"]; 90 | } else if (moveHorizontal == -1 && moveVertical == 0) { 91 | radians = anglesToRadiansConversionTable["angle270"]; 92 | } else if (moveHorizontal == -1 && moveVertical == -1) { 93 | radians = anglesToRadiansConversionTable["angle315"]; 94 | } 95 | 96 | } /* End orientationChanged(). */ 97 | 98 | 99 | } /* End class. */ 100 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/lib/generated/i18n.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | // ignore_for_file: non_constant_identifier_names 7 | // ignore_for_file: camel_case_types 8 | // ignore_for_file: prefer_single_quotes 9 | 10 | // This file is automatically generated. DO NOT EDIT, all your changes would be lost. 11 | class S implements WidgetsLocalizations { 12 | const S(); 13 | 14 | static const GeneratedLocalizationsDelegate delegate = 15 | GeneratedLocalizationsDelegate(); 16 | 17 | static S of(BuildContext context) => Localizations.of(context, S); 18 | 19 | @override 20 | TextDirection get textDirection => TextDirection.ltr; 21 | 22 | } 23 | 24 | class $en extends S { 25 | const $en(); 26 | } 27 | 28 | class GeneratedLocalizationsDelegate extends LocalizationsDelegate { 29 | const GeneratedLocalizationsDelegate(); 30 | 31 | List get supportedLocales { 32 | return const [ 33 | Locale("en", ""), 34 | ]; 35 | } 36 | 37 | LocaleListResolutionCallback listResolution({Locale fallback, bool withCountry = true}) { 38 | return (List locales, Iterable supported) { 39 | if (locales == null || locales.isEmpty) { 40 | return fallback ?? supported.first; 41 | } else { 42 | return _resolve(locales.first, fallback, supported, withCountry); 43 | } 44 | }; 45 | } 46 | 47 | LocaleResolutionCallback resolution({Locale fallback, bool withCountry = true}) { 48 | return (Locale locale, Iterable supported) { 49 | return _resolve(locale, fallback, supported, withCountry); 50 | }; 51 | } 52 | 53 | @override 54 | Future load(Locale locale) { 55 | final String lang = getLang(locale); 56 | if (lang != null) { 57 | switch (lang) { 58 | case "en": 59 | return SynchronousFuture(const $en()); 60 | default: 61 | // NO-OP. 62 | } 63 | } 64 | return SynchronousFuture(const S()); 65 | } 66 | 67 | @override 68 | bool isSupported(Locale locale) => _isSupported(locale, true); 69 | 70 | @override 71 | bool shouldReload(GeneratedLocalizationsDelegate old) => false; 72 | 73 | /// 74 | /// Internal method to resolve a locale from a list of locales. 75 | /// 76 | Locale _resolve(Locale locale, Locale fallback, Iterable supported, bool withCountry) { 77 | if (locale == null || !_isSupported(locale, withCountry)) { 78 | return fallback ?? supported.first; 79 | } 80 | 81 | final Locale languageLocale = Locale(locale.languageCode, ""); 82 | if (supported.contains(locale)) { 83 | return locale; 84 | } else if (supported.contains(languageLocale)) { 85 | return languageLocale; 86 | } else { 87 | final Locale fallbackLocale = fallback ?? supported.first; 88 | return fallbackLocale; 89 | } 90 | } 91 | 92 | /// 93 | /// Returns true if the specified locale is supported, false otherwise. 94 | /// 95 | bool _isSupported(Locale locale, bool withCountry) { 96 | if (locale != null) { 97 | for (Locale supportedLocale in supportedLocales) { 98 | // Language must always match both locales. 99 | if (supportedLocale.languageCode != locale.languageCode) { 100 | continue; 101 | } 102 | 103 | // If country code matches, return this locale. 104 | if (supportedLocale.countryCode == locale.countryCode) { 105 | return true; 106 | } 107 | 108 | // If no country requirement is requested, check if this locale has no country. 109 | if (true != withCountry && (supportedLocale.countryCode == null || supportedLocale.countryCode.isEmpty)) { 110 | return true; 111 | } 112 | } 113 | } 114 | return false; 115 | } 116 | } 117 | 118 | String getLang(Locale l) => l == null 119 | ? null 120 | : l.countryCode != null && l.countryCode.isEmpty 121 | ? l.languageCode 122 | : l.toString(); 123 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/lib/main.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | import "package:flutter/services.dart"; 3 | import "InputController.dart" as InputController; 4 | import "GameCore.dart"; 5 | 6 | 7 | void main() => runApp(FlutterHero()); 8 | 9 | 10 | /// Base widget. 11 | class FlutterHero extends StatelessWidget { 12 | @override 13 | Widget build(BuildContext context) { 14 | SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.bottom]); 15 | return MaterialApp(title : "FlutterHero", home : GameScreen()); 16 | } 17 | } 18 | 19 | 20 | /// Main widget. 21 | class GameScreen extends StatefulWidget { 22 | @override 23 | GameScreenState createState() => new GameScreenState(); 24 | } 25 | 26 | 27 | /// Main widget state. 28 | class GameScreenState extends State with TickerProviderStateMixin { 29 | 30 | 31 | /// Build main widget. 32 | @override 33 | Widget build(BuildContext inContext) { 34 | 35 | // Do one-time tasks that require the BuildContext (whether directly or not). 36 | if (gameLoopController == null) { 37 | firstTimeInitialization(inContext, this); 38 | } 39 | 40 | // The List of children for the Stack. 41 | List stackChildren = [ 42 | // Background. 43 | Positioned(left : 0, top : 0, 44 | child : Container(width : screenWidth, height : screenHeight, 45 | decoration : BoxDecoration(image : DecorationImage( 46 | image : AssetImage("assets/background.png"), fit : BoxFit.cover 47 | )) 48 | ) 49 | ), 50 | // Score. 51 | Positioned(left : 4, top : 2, 52 | child : Text('Score: ${score.toString().padLeft(4, "0")}', 53 | style : TextStyle(color : Colors.white, fontSize : 18, fontWeight : FontWeight.w900) 54 | ) 55 | ), 56 | // Energy bar. 57 | Positioned(left : 120, top : 2, width : screenWidth - 124, height : 22, 58 | child : LinearProgressIndicator(value : player.energy, backgroundColor : Colors.white, 59 | valueColor : AlwaysStoppedAnimation(Colors.red) 60 | ) 61 | ), 62 | // Crystal. 63 | crystal.draw() 64 | ]; 65 | 66 | // Add enemies. 67 | for (int i = 0; i < 3; i++) { 68 | stackChildren.add(fish[i].draw()); 69 | stackChildren.add(robots[i].draw()); 70 | stackChildren.add(aliens[i].draw()); 71 | stackChildren.add(asteroids[i].draw()); 72 | } 73 | 74 | // Now the planet and the player (must be done after enemies to ensure proper z indexing). 75 | stackChildren.add(planet.draw()); 76 | stackChildren.add(player.draw()); 77 | 78 | // Add any explosions that are currently exploding. 79 | for (int i = 0; i < explosions.length; i++) { 80 | stackChildren.add(explosions[i].draw()); 81 | } 82 | 83 | // Return the root widget. 84 | return Scaffold(body : GestureDetector( 85 | onPanStart : InputController.onPanStart, 86 | onPanUpdate : InputController.onPanUpdate, 87 | onPanEnd : InputController.onPanEnd, 88 | child : Stack(children : stackChildren) 89 | )); /* End Scaffold. */ 90 | 91 | } /* End build. */ 92 | 93 | 94 | } /* End class. */ 95 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_hero 2 | description: FlutterHero 3 | 4 | version: 1.0.0+1 5 | 6 | environment: 7 | sdk: ">=2.1.0 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | cupertino_icons: ^0.1.2 13 | audioplayers: 0.11.0 14 | 15 | dev_dependencies: 16 | flutter_test: 17 | sdk: flutter 18 | 19 | flutter: 20 | uses-material-design: true 21 | assets: 22 | - assets/ 23 | -------------------------------------------------------------------------------- /ch_09/flutter_hero/res/values/strings_en.arb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/practical-flutter/910d5fd7ab52fa21486dcbd1ddf51a0e722418e0/ch_09/flutter_hero/res/values/strings_en.arb -------------------------------------------------------------------------------- /ch_09/flutter_hero/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:flutter_hero/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(FlutterHero()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /errata.md: -------------------------------------------------------------------------------- 1 | # Errata for *Book Title* 2 | 3 | On **page xx** [Summary of error]: 4 | 5 | Details of error here. Highlight key pieces in **bold**. 6 | 7 | *** 8 | 9 | On **page xx** [Summary of error]: 10 | 11 | Details of error here. Highlight key pieces in **bold**. 12 | 13 | *** -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | Hello and welcome to the source code for the book 2 | Practical Flutter by Frank W. Zammetti! 3 | 4 | Each chapter that has code associated with it (not all do) has it's own 5 | directory. Each source file in a given directory, for the most part, 6 | is a full program that you can either run in DartPad (for the Dart-only 7 | chapters) or that you can drop in main.dart of a newly-created Flutter app 8 | and run (there may be a few exceptions to this, but it should mostly be true). 9 | 10 | Chapters 5 and 6 combined cover the FlutterBook app. 11 | Chapters 7 and 8 combined cover the FlutterChat app. 12 | Chapter 9 is the FlutterHero game app. 13 | 14 | Each of the apps is an Android Studio project that you SHOULD be able to just 15 | load up and play with right away. 16 | 17 | All of this code was developed and confirmed working under the following 18 | versions: 19 | 20 | Flutter: 1.5.4-hotfix.2 (stable channnel) 21 | Framework: revision 7a4c33425d 22 | Engine: revision 52c7a1e849 23 | Tools: Dart 2.3.0 (build 2.3.0-dev.0.5 a1668566e5) 24 | Android Studio: 3.4 25 | Android toolchain: Android SDK version 28.0.3 26 | 27 | While it all SHOULD work on newer versions, or even some older one, the author 28 | does not warrant this code on any versions other than those listed. 29 | 30 | Thank you very much for purchasing and reading my book! Take care! 31 | 32 | This code is released under the terms of the following license: 33 | 34 | The MIT License 35 | 36 | Copyright (c) 2019 Frank W. Zammetti 37 | 38 | Permission is hereby granted, free of charge, to any person obtaining a copy 39 | of this software and associated documentation files (the "Software"), to deal 40 | in the Software without restriction, including without limitation the rights 41 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 42 | copies of the Software, and to permit persons to whom the Software is 43 | furnished to do so, subject to the following conditions: 44 | 45 | The above copyright notice and this permission notice shall be included in 46 | all copies or substantial portions of the Software. 47 | 48 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 49 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 50 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 51 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 52 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 53 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 54 | THE SOFTWARE. 55 | 56 | Frank W. Zammetti 57 | 6/26/2019 58 | --------------------------------------------------------------------------------