Table of Content Introduction 5 Flutter Installation 6 Application Folder Structure 10 Dart Basics 11 Variables 11 Defining a map 14 Functions 15 Named parameter 16 Default value parameter 16 Control flow 17 Loops 17 For Loop 17 While loop 18 Do-while loop 18 Switch 18 Exception handling 19 Flutter - Widgets 21 What is a widget? 21 Platform specific widgets 22 Material Widgets 22 Cupertino Widgets 23 Layout Widgets 23 Single Child Widgets 24 Multi Child Widgets 24 Example of widgets and Layout widgets 25 Image 26 Load Network Image 26 Load Image from Assets 27 Icon 29 Buttons 30 Button Examples 31 Multi Child Layouts 35 Flutter - Linear Layout 35 Framelayout in Flutter 38 2
Flex Widget 39 Like Row Widget 41 Like Column Widget 42 Weight Property like Android in Flex widget 43 Listview 44 How will we handle the item click events? 45 Dynamic ListView 46 Listview.separated 48 Examples of Single Child Layout Widgets 50 Container 50 Card 52 Expanded 54 Flexible 57 Center 58 GestureDetector 58 Positioned 61 SafeArea 64 SingleChildScrollView 64 Themes 67 Scaffold 68 Dialogs 73 Simple AlertDialog 73 Add buttons to Alert Dialogs 74 How to close Dialog 74 ExpansionPanelList & ExpansionPanel 78 ExpansionPanel 78 ExpansionPanelList 78 GridView 83 PopupMenu 86 Checked Widgets 88 CheckBox 88 CheckboxListTile 89 Radio 89 TabBar TabBarView 95 Table 97 Future and FutureBuilder 101 FutureBuilder 102 3
StreamBuilder 105 Navigation 108 Routing table (Named Routes) 113 Navigation switch animation 117 Form & Form Fields 118 Form State 118 Form Validation 119 Input Decoration Themes 123 Networking & JSON and Serialization 126 HTTP Introduction 126 HTTPClient 126 Make HTTPRequest 126 Decode and encode JSON 127 HTTP Library 131 CompleteExample 135 Database and local storage 141 SharedPreferences 141 How to access Shared Preferences in Flutter? 141 Database 145 How to access SQLite in Flutter? 145 SQFlite Features 145 Database operations 147 Create Table 147 Query 149 State Management 161 Stateful Widget 161 InheritedWidget 162 BLoC 165 What is BLoC? 165 Example 166 Firebase Integration 169 Firebase Setup 169 Firebase authentication & Google sign in using Flutter 174 Chat Application with Firebase database Flutter 181 4
Introduction Flutter is an open-source UI software development kit created by Google. It is used to develop applications for Android, iOS, Windows, Mac, Linux, Google Fuchsia and the web. Flutter is Google's mobile UI framework that can quickly build high-quality native user interfaces on iOS and Android. Flutter works with existing code. Flutter is being used by more and more developers and organizations around the world, and Flutter is completely free and open source . At present, some modules of the company are developed using Flutter . The major components of Flutter include: Dart platform Flutter engine Foundation library Design-specific widgets Dart Platform Flutter apps are written in the Dart language and make use of many of the language's more advanced features You can refer Dart Language at Dart 5
Flutter Installation Flutter is supporting HybridApp development on different Os. To set up the flutter on each individual os by this Flutter official Tutorial In this section we will learn how to install Flutter SDK in Windows system. Step 1: Download Flutter SDK from Official website The Latest version is 1.12 Step 2: Unzip and archive file in specific folder, like c:flutter Step 3: Update the system path to include flutter bin directory Step 4: Open terminal and move to installed directory and run flutter doctor flutter doctor is tool to check all requirements for the flutter is installed or not and show the details The result of the above command will give you Doctor summary (to see all details, run flutter doctor -v): [√] Flutter (Channel master, v1.14.6-pre.51, on Microsoft Windows [Version 6.3.9600], locale en-IN) [√] Android toolchain - develop for Android devices (Android SDK version 28.0.3) [√] Chrome - develop for the web [√] Android Studio (version 3.3) [!] Android Studio (version 3.4) 6
X Flutter plugin not installed; this adds Flutter specific functionality. X Dart plugin not installed; this adds Dart specific functionality. [√] Connected device (2 available) ! Doctor found issues in 1 category. Step 5: Install Android Studio Step 6: Check Latest Android SDK installed or not, if not install it Step 7: Open Android studio and install Dart and Flutter Plugins for Android studio. ● Click File > Settings > Plugins. ● Select the Flutter plugin and click Install. ● Click Yes when prompted to install the Dart plugin. ● Restart Android studio Flutter – Creating Simple Application in Android Studio Now Open File -> Create new Flutter Project It will prompt below screen 7
Select Flutter application and press Next Now it will ask below details 8
● Enter your Project Name ● Set Flutter SDk path (It is installation path) ● Set the Project Location ● Press Next Button Enter Domain name and Press Finish Now it will create a project with auto generated code Now connect real device/Emulator and run the application The output will be like this 9
Application Folder Structure To understand flutter fully we need to understand the first Flutter folder structure. ● android - Auto generated source code to create android application ● ios - Auto generated source code to create ios application ● web- Auto generated source code to create web application ● lib - Main folder containing Dart code written using flutter framework ● lib/main.dart - Entry point of the Flutter application ● test - Folder containing Dart code to test the flutter application ● test/widget_test.dart - Sample code ● .gitignore - Git version control file ● .metadata - auto generated by the flutter tools ● .packages - auto generated to track the flutter packages ● .iml - project file used by Android studio ● pubspec.yaml - Used by Pub, Flutter package manager ● pubspec.lock - Auto generated by the Flutter package manager, Pub ● README.md - Project description file written in Markdown format 10
Dart Basics Dart is an open-source general-purpose programming language. It was originally developed by Google. Dart is an object-oriented language with C-style syntax. It supports programming concepts like interfaces, classes, unlike other programming languages Dart doesn’t support arrays. Dart collections can be used to replicate data structures such as arrays, generics, and optional typing. The following code shows a simple Dart program void main() { print('Hello, World!'); } Every Dart application contains main() functions, from here code will execute. Variables Variable is named storage location and Data types simply refers to the type and size of data associated with variables and functions. Dart uses a var keyword to declare the variable. The syntax of var is defined below var name = 'Flutter'; Dart provide us various built in data types Numbers 11
As Other Programming languages to Java or C++ Dart does not have anything like float or long. Dart offers just two types of number Int Double Strings : It represents a sequence of characters. String values are specified in either single or double quotes Booleans : Dart uses the bool keyword to represent Boolean values – true and false Lists & Maps : It is used to represent a collection of objects Runes : Represents string Unicode coded characters (UTF-32 code points), etc Symbols : Use a Symbol literal to obtain the symbol's symbol object, which is to add a # symbol in front of the identifier String name = 'Flutter'; Here String is Data type, name is variable name and Flutter is value of variable var name = 'Flutter'; The type of the name variable is inferred as String. The compiler will check we have var keyword not String so type depends on value which in this case is String Example: void main() { String fName = 'Chandu'; var lName='Mouli'; int intValue = 123; print(fName); print(lName); 12
print(intValue); } Output will be Chandu Mouli 123 List : Declare a list is very simple, you can simply use square brackets [] to define the list. The following are common operations for lists main(List<String> args) { var list = [1,2,3,4]; print(list); //Output: [1, 2, 3, 4] //Length print(list.length); //Selecting single value print(list[1]); //Output: 2 //Adding a value list.add(10); //Removing a single instance of value list.remove(3); //Remove at a particular position list.removeAt(0); } If we want to define a compile-time constant list, for example, the contents of the list are immutable, you can use keywords const 13
main(List<String> args) { var list = const [1,2,3,4]; } Defining a map We can define maps by using curly braces {} main(List<String> args) { var map = { 'key1': 'value1', 'key2': 'value2', 'key3': 'value3' }; //Fetching the values print(map['key1']); //Output: value1 print(map['test']); //Output: null //Add a new value map['key4'] = 'value4'; //Length print(map.length); //Check if a key is present map.containsKey('value1'); //Get entries and values var entries = map.entries; var values = map.values; } We can also define Map by constructor 14
main(List<String> args) { var squares = new Map(); squares[4] = 16; } Functions Functions in dart are similar to those in JavaScript. It consists of the function name, return value, and parameters. main(List<String> args) { var name = fullName('Chandu', 'Mouli'); print(name); } String fullName(String firstName, String lastName) { return "$firstName $lastName"; } return type is an option, if we can remove return type and function looks like below main(List<String> args) { var name = fullName('Chandu', 'Mouli'); print(name); } fullName(String firstName, String lastName) { return "$firstName $lastName"; } 15
If the function having single line we can write it as main(List<String> args) { var name = fullName('Chandu', 'Mouli'); print(name); } fullName(String firstName, String lastName) => "$firstName $lastName"; Named parameter Dart provides Named parameters while calling the functions main(List<String> args) { var name = fullName(firstName: 'Chandu', lastName: 'Mouli'); print(name); } fullName({String firstName, String lastName}) { return "$firstName $lastName"; } Default value parameter If we didn't pass parameter to function call we can define a default value to the parameters main(List<String> args) { 16
var name = fullName(firstName: 'Chandu'); print(name); } fullName({String firstName, String lastName = "Shekar"}) { return "$firstName $lastName"; } Control flow If - else This is similar to other programing languages If - else main(List<String> args) { var day = 6; if (number > 7) { print('Not a Week Day'); } else if (number < 100) { print('Week Day'); } } Loops For Loop main(List<String> args) { for (int i = 0; i < 10; i++) { print('$i'); } } 17
While loop main(List<String> args) { int i = 0; while(i < 10) { print('$i'); i++; } } Do-while loop main(List<String> args) { int i = 0; do { print('$i'); i++; } while (i < 10); } Switch main(List<String> args) { int day = 5; switch(age) { case 1: print('Sunday.'); break; case 2: print('Monday.'); break; 18
case 3: print('Tuesday'); break; case 4: print('Wednesday'); break; case 5: print('Thursday'); break; case 6: print('Friday'); break; case 7: print('Saturday'); break; } } Exception handling Similar to other programing languages dart also we can handle exceptions by Try, catch blocks and throw exceptions by throw keyword main(List<String> args) { divide(10, 0); } divide(int a, int b) { if (b == 0) { throw new IntegerDivisionByZeroException(); } return a / b; } 19
Let’s catch the exception pass catch block main(List<String> args) { try { divide(10, 0); } on IntegerDivisionByZeroException { print('Division by zero.'); } } divide(int a, int b) { if (b == 0) { throw new IntegerDivisionByZeroException(); } return a / b; } 20
Flutter - Widgets Now you are not having knowledge on flutter basics then go with Technical overview What is a widget? Everything within a flutter application is widget. From basic "text","Buttons" to "Screen Layouts". In flutter application everything is designed by widget only. These widgets are arranged in hierarchical order to be displayed onto the screen. In the Flutter widgets most of widgets are Container widgets unlike Text widget Widgets are two types ● Stateless Widget ● Stateful widget All Widgets are categorized into below groups 1. Platform specific widgets 2. Layout widgets 3. State maintenance widgets 4. Platform independent / basic widgets 21
Platform specific widgets Flutter provides platform specific widgets like Android and Ios Android specific widgets are designed based on Material design rules These widgets are called Material Widgets Ios specific widgets are designed based on Human Interface Guidelines by Apple, These widgets are called Cupertino widgets Material Widgets Scaffold AppBar BottomNavigationBar TabBar TabBarView ListTile RaisedButton FloatingActionButton FlatButton IconButton DropdownButton PopupMenuButton ButtonBar TextField Checkbox Radio Switch Slider Date & Time Pickers 22
SimpleDialog AlertDialog Cupertino Widgets CupertinoButton CupertinoPicker CupertinoDatePicker CupertinoTimerPicker CupertinoNavigationBar CupertinoTabBar CupertinoTabScaffold CupertinoTabView CupertinoTextField CupertinoDialog CupertinoDialogAction CupertinoFullscreenDialogTransition CupertinoPageScaffold CupertinoPageTransition CupertinoActionSheet CupertinoActivityIndicator CupertinoAlertDialog CupertinoPopupSurface Layout Widgets Layout widgets are widgets which will arrange the widget on the screen. Examples: Container,Column,Row,Stack.... 23
Single Child Widgets Container Padding ConstrainedBox Baseline FractinallySizedBox IntrinsicHeight IntrinsicWidth LimitedBox OffStage OverflowBox SizedBox SizedOverflowBox Transform CustomSingleChildLayout Align Multi Child Widgets Row Column ListView GridView Expanded Table Flow Stack 24
Example of widgets and Layout widgets First create a simple visible widget Text("Text widget") If we want to add this visible widget to the Screen we need to put this widget inside the layout widget. Let's create layout widget Container( child: Text("Text Widget") ) Now let;s add this Lyoutwidget to the screen by class Sample1 extends StatelessWidget{ @override Widget build (BuildContext context) { return Container( child: Text("Text Widget"); ) } } 25
Image Image widget used to show an image. When displaying an image, you specify the image source in the constructor: image provider asset, network, file, memory Load Network Image class ImageWidget extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( home: SafeArea( child: Scaffold( body: Center(child: Image.network("https://cdn.pixabay.com/photo/2016/07/0 3/16/06/global-warming-1494965_960_720.jpg",width: 200,)) ), ), ); } } 26
Load Image from Assets To load images from assets first we need to create an Assets folder inside the application. It could be like below Now after adding image into assets folder we need to set the path inside pubspec.yaml file 27
Next run the below command in terminal to configure image flutter packages get Now lets create sample class ImageWidget extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( home: SafeArea( child: Scaffold( body: Center(child: Image.asset("assets/user.png",width: 200,)) ), ), ); } } While Loading the images there is no option to show placeholder with above way, Then how to show Placeholder while loading the image. With FadeInImage widget we can achieve to show placeholder image Replace above code with Scaffold( body: Center(child: FadeInImage.assetNetwork(placeholder: "assets/user.png", image: "https://cdn.pixabay.com/photo/2016/07/03/16/06/globa l-warming-1494965_960_720.jpg",width: 200,)) ) 28
Icon The icon widget allows us to quickly build icon widgets using a pre-built list of material icons, available in the Icons class. We can specify the icon size and color class IconWidget extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( home: SafeArea( child: Scaffold( 29
body: Center(child: Icon(Icons.email,color: Colors.pink,size: 48,)) )) , ); } } Buttons We can’t imagine a programming language without click events. Similarly other languages flutter provided buttons to handle click events. We have different types of buttons in flutter FlatButton RaisedButton IconButton OutlineButton DropdownButton BackButton CloseButton FloatingActionButton All these buttons click event is handled by onPressed() onPressed: (){ } FlatButton : This type of button doesn't have any border, When we click on it it will show press effect RaisedButton : When we need to show some decoration we can use this button 30
IconButton : It is a material button, Flashes background circle when clicked on OutlineButton : A bordered button whose elevation increases and whose background becomes opaque when the button is pressed DropDownButton : It is a Material widget, Which is used for selecting from a list of items It similar to Spinner in Android CloseButton : An IconButton setup for use as a close button to close modals (or any other closeable content). Flashes background circle when clicked on FloatingActionButton: It is a Material widget button, A button that hovers in a layer above content Button Examples class ButtonWidget extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return ButtonsWidgetState(); } } class ButtonsWidgetState extends State<ButtonWidget>{ var selected_item="Please choose a location"; List<String>list=[ "Please choose a location", "Item One", "Item Two", "Item Three", "Item Four", ]; 31
@override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( home: SafeArea(child: Scaffold( appBar: AppBar(title: Text("Buttons"),backgroundColor: Colors.pink,), body: Container( child: Center( child: Column( children: <Widget>[ FlatButton(onPressed: (){ debugPrint('Button Clicked '); }, child: Text("Flat Button")), RaisedButton(onPressed: (){ debugPrint('Button Clicked '); },child: Text("Raised Button"),), OutlineButton(onPressed: (){ debugPrint('Button Clicked '); },child: Text("Outline Button"),highlightedBorderColor: Colors.pink,), IconButton(onPressed: (){ debugPrint('Button Clicked '); },icon: Icon(Icons.add),color: Colors.pink,), DropdownButton( items:list.map((value){ return DropdownMenuItem(child: Text(value),value: value); }).toList(), hint: Text("Please choose a location"), value: selected_item, onChanged: (value){ 32
selected_item=value; setState(() { }); debugPrint('Changed: ${value}'); }, ) BackButton(onPressed: (){ debugPrint('Button Clicked '); },color: Colors.pink,), CloseButton(), FloatingActionButton(onPressed: (){ debugPrint('Button Clicked '); }, child: Icon(Icons.search),backgroundColor: Colors.pink,) ], ), ), ), )), ); } } 33
34
Multi Child Layouts Flutter - Linear Layout In Android we have a linear layout to arrange childs in horizontal and vertical, similarly in flutter we can arrange by Row, Column widgets. Example Horizontal Arrangement by Row class RowWidget extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Tutorial', theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( body: SafeArea(child: Container( color: Colors.brown, child: Row( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Text("Header",style: TextStyle(color: Colors.white),), Icon(Icons.account_circle,size: 100,color: Colors.white,), Text("Name ",style: TextStyle(color: Colors.white)) ], ), )), ), 35
); } } Vertical Arrangement by Column class ColumnWidget extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Tutorial', theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( body: SafeArea(child: Container( color: Colors.brown, child: Column( mainAxisSize: MainAxisSize.max, children: <Widget>[ Text("Header",style: TextStyle(color: Colors.white),), Icon(Icons.account_circle,size: 100,color: Colors.white,), Text("Name ",style: TextStyle(color: Colors.white)) ], ), )), ), ); } } 36
We can see the arrangement of children horizontal/vertical in below screen Row wrap_content Row match_parent Column wrap_content Column match_parent How to set the Gravity for these widgets We can set the Gravity by using CrossAxisAlignment How it will work for Row and Column widgets If we set the property for the Row it will align based on Vertical direction(center,start,end…) If we set the property for Column it will align based on Horizontal direction(center,start,end…) 37
Framelayout in Flutter Flutter uses Stack widgets to control child widgets at a layer. Child widgets can completely or partially cover the base widgets. Stack control positions its children relative to the edges of its box. This class is useful if you just want to overlap multiple child widgets. class StackWidget extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Tutorial', theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( body: SafeArea(child: Center( child: Stack( alignment: const Alignment(0, 0), children: <Widget>[ Image.network("https://cdn.pixabay.com/photo/2017/04/23 /19/17/climate-change-2254711_960_720.jpg"), Container( decoration: BoxDecoration( color: Colors.white, ), child: Text('GLobal Warming',style: TextStyle(fontSize: 20),), ), ], 38
), )), ), ); } } Flex Widget The Flex Widget is similar to Row and Column widget. We can use it as Row and Column by specifying the direction property. class FlexWidget extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Tutorial', theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( body: SafeArea(child: Container( color: Colors.brown, child: Flex( direction: Axis.vertical, mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Container(child: Padding( padding: const EdgeInsets.all(8.0), child: Text("Header",style: TextStyle(color: Colors.white),), 39
),color: Colors.green,), Container(child: Icon(Icons.account_circle,size: 100,color: Colors.white,),color: Colors.yellow,), Container(child: Padding( padding: const EdgeInsets.all(8.0), child: Text("Name ",style: TextStyle(color: Colors.white)), ),color: Colors.pink,) ], ), ), ), ), ); } } Change different alignment and check the result 40
Like Row Widget Direction Horizontal wrap_content Direction Horizontal match_parent 41
Like Column Widget Direction Vertical wrap_content Direction Vertical match_parent 42
Weight Property like Android in Flex widget If we add more items inside flex , to fit all these we can use Expandable widget to set each child flex . Flex( direction: Axis.horizontal, mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Flexible( flex: 1, child: Container(child: Padding( padding: const EdgeInsets.all(8.0), child: Text("Header",style: TextStyle(color: Colors.white),), ),color: Colors.green,), ), Flexible(flex: 1,child: Container(child: Icon(Icons.account_circle,size: 100,color: Colors.white,),color: Colors.yellow,)), Flexible(flex: 1, child: Container(child: Padding( padding: const EdgeInsets.all(8.0), child: Text("Name ",style: TextStyle(color: Colors.white)), ),color: Colors.pink,), ), ], ) 43
Listview If we have more items to make them scrollable if the screen of the user device is smaller than the content of the control. In Flutter, the easiest way is to use ListView Here simple listview example class ListViewWidget extends StatefulWidget { ListViewWidget({Key key}) : super(key: key); @override ListViewWidgetState createState() => ListViewWidgetState(); } class ListViewWidgetState extends State<ListViewWidget> { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( backgroundColor: Colors.pink, title: Text("Listview"), ), body: _getListData(), ), ); } _getListData() { List<Widget> widgets = []; for (int i = 0; i < 100; i++) { widgets.add( Card( margin: EdgeInsets.all(5), 44
child: ListTile( title: Text("Row $i"), leading: Icon(Icons.account_circle), trailing: Icon(Icons.arrow_forward_ios,size: 14,), ), )); } return ListView(children: widgets); } } How will we handle the item click events? ListTile has the property of onTap() function, with this we can handle the Click events of each child item. onTap: (){ _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text("Clicked on Child $i"))); }, 45
Dynamic ListView The above example shows all static static widgets data. If we want to show dynamic data then we need to use ListView.Builder() class ListViewWidget extends StatefulWidget { ListViewWidget({Key key}) : super(key: key); 46
@override ListViewWidgetState createState() => ListViewWidgetState(); } class ListViewWidgetState extends State<ListViewWidget> { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( backgroundColor: Colors.pink, title: Text("Listview"), ), body: _getDynamicList(), ), ); } _getDynamicList() { var countries = ['Albania', 'Andorra', 'Armenia', 'Austria', 'Azerbaijan', 'Belarus', 'Belgium', 'Bosnia and Herzegovina', 'Bulgaria', 'Croatia', 'Cyprus', 'Czech Republic', 'Denmark', 'Estonia', 'Finland', 'France', 'Georgia', 'Germany', 'Greece', 'Hungary', 'Iceland', 'Ireland', 'Italy', 'Kazakhstan', 'Kosovo', 'Latvia', 'Liechtenstein', 'Lithuania', 'Luxembourg', 'Macedonia', 'Malta', 'Moldova', 'Monaco', 'Montenegro', 'Netherlands', 'Norway', 'Poland', 'Portugal', 'Romania', 'Russia', 'San Marino', 'Serbia', 'Slovakia', 'Slovenia', 'Spain', 'Sweden', 'Switzerland', 'Turkey', 'Ukraine', 'United Kingdom', 'Vatican City']; return ListView.builder( itemCount: countries.length, itemBuilder: (ctx,index){ return ListTile( onTap: (){ _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text("Clicked on Country ${countries[index]}"))); 47
}, title: Text(countries[index]), leading: Icon(Icons.flag), trailing: Icon(Icons.arrow_forward_ios,size: 14,), ); }); } } Listview.separated class ListViewBuilderWidget extends StatefulWidget{ @override 48
State<StatefulWidget> createState() { return new _ListViewBuilderWidget (); } } class _ListViewBuilderWidget extends State<ListViewBuilderWidget>{ @override Widget build(BuildContext context) { return Scaffold( appBar: new AppBar( title: new Text("ListviewBuilder Widget"), ), body: ListView.separated( itemCount: 100, itemBuilder: (BuildContext context, int index) { return ListTile(title: Text(" $index - ", style: TextStyle(color: Colors.blue),)); }, separatorBuilder: (BuildContext context, int index) { return Divider(color: Colors.blue, height: 10,); } ), ); } } 49
Examples of Single Child Layout Widgets Container A convenience widget that combines common painting, positioning, and sizing widgets. Often used to contain wrap child widgets and apply styling Container having the below properties Color Property Child Property Alignment Property Constraints Property Margin Property Padding Property Decoration Property ForegroundDecoration Property Transform Property class ContainerWidget extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( home: Scaffold( appBar: AppBar(title: Text("Container"),), body: Container( color: Color.fromARGB(255, 66, 165, 245), child: Container( color: Colors.pink, alignment: Alignment.center, constraints: BoxConstraints( maxHeight: 300, 50
maxWidth: 200, minWidth: 150, minHeight: 150, ), child: Container( child: Text("Flutter Cheatsheet", style: TextStyle( fontSize: 30.0, color: Colors.white, ),),), transform: Matrix4.rotationZ(0.5), ), alignment: Alignment.center, ),),); }} 51
Card A card-like widget. Similar to Android's CardView, the card has slightly rounded corners and shadows Card Widget attribute color : container background color elevation : Z-axis height, to achieve the shadow effect. shape : defines the shape of the container margin : margin clipBehavior : the way to clip content Example: class CardWidget extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( home: Scaffold( appBar: AppBar(title:Text("Card Widget"),backgroundColor: Colors.pink,), body: Container( alignment: Alignment.topCenter, margin: EdgeInsets.only(top: 10.0), child: SizedBox( width: 400.0, height: 200.0, child: Card( color: Colors.purple, elevation: 30.0, child: Padding( padding: EdgeInsets.all( 14.0, 52
), child: Column( children: <Widget>[ Row( children: <Widget>[ CircleAvatar( backgroundImage: NetworkImage( "https://cdn.pixabay.com/photo/2016/10/17/17/41/priyan ka-chopra-1748248_960_720.jpg"), radius: 24.0, ), Container( margin: EdgeInsets.only(left: 10.0), child: Text( "Text", style: TextStyle(color: Colors.white, fontSize: 20.0), ), ), ], ), Container( margin: EdgeInsets.only(top: 30.0), child: Text( "Never Stop Thinking...", style: TextStyle(color: Colors.white, fontSize: 30.0), ), ), Container( alignment: Alignment.bottomRight, margin: EdgeInsets.only(top: 30.0), child: Text( "2020-01-10 15:47:35", style: TextStyle(color: Colors.white, fontSize: 14.0), ), ), 53
], ), ), ), ), ), ), ); } } Expanded The Expanded component allows Row, Column, Flex and other sub-components to expand in the direction of their main axis and fill the available space. Similar usage of widget properties in Android 54
● Expanded will be distributed as full as possible in the main axis direction of Row, Column, or Flex ● If Column contains two childs and two widgets are expanded, both share the available vertical space evenly. ● If only one is expanded, the expanded one takes up all the available vertical space. ● If neither is expanded, the available vertical space goes unfilled Example: class ExpandedWidget extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( home: Scaffold( appBar: AppBar(backgroundColor: Colors.pink,title: Text("Expanded Widget"),), body: new Row( children: <Widget>[ new Expanded( flex: 2, child: new Container( child: new Text('Text1', textAlign: TextAlign.center), height: 100, alignment: AlignmentDirectional.center, color: Colors.yellow, ), ), new Expanded( 55
flex: 1, child: new Container( child: new Text('Text2', textAlign: TextAlign.center), height: 100, alignment: AlignmentDirectional.center, color: Colors.lightGreen, ), ), new Expanded( flex: 1, child: new Container( child: new Text('Text3', textAlign: TextAlign.center), height: 100, alignment: AlignmentDirectional.center, color: Colors.deepPurple, ), ), ], ), )); } } 56
Flexible It is actually Expanded inheritance Flexible. Use Flexible widgets to Row, Column or Flex provided in the main shaft expands to fill the available space in flexibility (e.g., horizontal filling sub assembly Row or perpendicular filled Column), but Expandeddifferent, Flexible is not required to fill the available space sub components. Flexible The control must be Row, Column or Flex a descendant of Row, the path from the control to its closure , Column or Flex the path must contain only Stateless Widgets or Stateful Widget these, and cannot be other types of widgets (for example Render ObjectWidget) 57
Center This widget is used to center a Widget within its parent Widget. GestureDetector GestureDetector is a widget that detects gestures. If the child property of GestureDetector is not empty, GestureDetector sets its size to the size of the child. If the child property is empty, it sets its size to the size of the parent component We have different type of gestures, below are few of them ● onTapDown, ● onTapUp, ● onTap, ● onTapCancel, ● onForcePressPeak, ● onForcePressUpdate, ● onForcePressEnd, ● onPanDown, ● onPanStart, ● onPanUpdate, ● onPanEnd, ● onPanCancel, ● onScaleStart, ● onScaleUpdate, ● onScaleEnd 58
● onSecondaryTapDown, ● onSecondaryTapUp, ● onSecondaryTapCancel, ● onDoubleTap, ● onLongPress, ● onLongPressStart, ● onLongPressMoveUpdate, ● onLongPressUp, ● onLongPressEnd, Example class GesterDetectorWidget extends StatelessWidget{ GlobalKey<ScaffoldState>_scaffoldstate=GlobalKey(); @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( home: Scaffold( key: _scaffoldstate, appBar: AppBar(title:Text("Card Widget"),backgroundColor: Colors.pink,), body: Container( child: GestureDetector( child: Center( child: Container( color: Colors.pink, child: Padding( padding: const EdgeInsets.all(8.0), child: Text("Gesture Me",style: TextStyle(fontSize: 20,color: Colors.white),), ), )), 59
onTap: (){ _scaffoldstate.currentState.showSnackBar(SnackBar(content: Text("onTap Event"))); }, onDoubleTap: (){ _scaffoldstate.currentState.showSnackBar(SnackBar(content: Text("onDoubleTap Event"))); }, onLongPress: (){ _scaffoldstate.currentState.showSnackBar(SnackBar(content: Text("onLongPress Event"))); }, ), ) ) ); } } 60
Positioned This use controls the position of the widget, through which he can place a component at will, a bit like an absolute layout Positioned({ Key key, this.left, this.top, this.right, this.bottom, this.width, this.height, @required Widget child, }) 61
Example class PositionedWidget extends StatelessWidget{ GlobalKey<ScaffoldState>_scaffoldstate=GlobalKey(); @override Widget build(BuildContext context) { final size = MediaQuery.of(context).size; return MaterialApp( home: Scaffold( key: _scaffoldstate, appBar: AppBar(title:Text("Positioned Widget"),backgroundColor: Colors.pink,), body:Container( width: size.width, height: size.height, child: Stack( children: <Widget>[ Positioned( child: CircleAvatar( backgroundImage: NetworkImage("https://cdn.pixabay.com/photo/2016/10/17/17/4 1/priyanka-chopra-1748248_960_720.jpg"), radius: 80.0, ), right:10, top: 10, ), Positioned( child: CircleAvatar( backgroundImage: NetworkImage("https://cdn.pixabay.com/photo/2016/10/17/17/4 1/priyanka-chopra-1748248_960_720.jpg"), radius: 80.0, ), left: size.width / 2 * 0.8, top: size.height / 2 * 0.7, 62
), Positioned( child: CircleAvatar( backgroundImage: NetworkImage("https://cdn.pixabay.com/photo/2016/10/17/17/4 1/priyanka-chopra-1748248_960_720.jpg"), radius: 80.0, ), left: 10, bottom: 10, )], ), ),) ); }} 63
SafeArea A widget that insets its child by sufficient padding to avoid intrusions by the operating system SafeArea is mainly used to ensure that the view will not be covered by system components, such as the status bar, etc SafeArea Constructor is like below const SafeArea({ Key key, this.left = true, this.top = true, this.right = true, this.bottom = true, this.minimum = EdgeInsets.zero, this.maintainBottomViewPadding = false, @required this.child, }) SingleChildScrollView This Widget is used to show a child Widget even if there is not enough space to view the entirety of the child Widget SingleChildScrollView is similar to scrollview in Android, and it can only contain one child element const SingleChildScrollView({ Key key, this.scrollDirection = Axis.vertical, 64
this.reverse = false, this.padding, bool primary, this.physics, this.controller, this.child, this.dragStartBehavior = DragStartBehavior.down, }) key : the unique identifier of the current element (similar to id in Android) scrollDirection : scroll direction, default is vertical reverse : whether to slide in the opposite direction of the reading direction. padding : padding distance primary : Whether to use the default Primary ScrollController in the widget tree. When the sliding direction is vertical (scrollDirection value is Axis.vertical) and the controller is not specified, the primary defaults to true physics : This property accepts a ScrollPhysics object, which determines how the scrollable widget responds to user operations, such as the user continuing to perform an animation after lifting the finger, or how to display it when sliding to the boundary. controller : This property accepts a ScrollController object. The main role of ScrollController is to control the scroll position and listen for scroll events child : child element class SingleChildScrollViewWidget extends StatelessWidget{ var alphabets="ABCDEFGHIJKLMNOPQRSTUVWXYZ"; @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( home: Scaffold( 65
appBar: AppBar(title:Text("SingleChildScroll Widget"),backgroundColor: Colors.pink,), body: horizontalScroll() )); } horizontalScroll() { return SingleChildScrollView( scrollDirection: Axis.horizontal, child: Center( child: Row( children: alphabets.split("").map((a)=>Padding( padding: const EdgeInsets.all(8.0), child: Text(a,style: TextStyle(fontSize: 20,color: Colors.pink),), )).toList(), ), ), ); } verticalScroll() { return SingleChildScrollView( scrollDirection: Axis.vertical, child: Center( child: Column( children: alphabets.split("").map((a)=>Padding( padding: const EdgeInsets.all(8.0), child: Text(a,style: TextStyle(fontSize: 20,color: Colors.pink),), )).toList(), ), ), ); } } 66
Vertical Direction Horizontal Direction Themes When we build a Flutter app, we build a root Widget. That Widget usually returns a MaterialApp, which builds the foundations for the app. One of the constructor arguments for MaterialApp is the Theme object. This object specifies the colors to be used in the application’s Widgets. As you can see below the user can pass in Theme data into the MaterialApp constructor using a ThemeData object Change Dynamic theme Find example here http://rrtutors.com/description/27 67
Scaffold Scaffold is the page display framework Scaffold has different attributes to handle the Pages appBar: An AppBar displayed at the top of the interface, which is the ActionBar and Toolbar in Android body: the main content widget displayed in the current interface floatingActionButton: FAB defined in paper and ink design, the main function button of the interface persistentFooterButtons: Buttons that are fixed to the bottom, such as OK and Cancel buttons below the dialog box drawer: sidebar control backgroundColor: The background color of the content. The default value is ThemeData.scaffoldBackgroundColor. bottomNavigationBar: the navigation bar displayed at the bottom of the page resizeToAvoidBottomPadding: similar to android: windowSoftInputMode = ”adjustResize” in Android, controls whether the content body of the interface is rearranged to avoid the bottom being covered, for example, when the keyboard is displayed, the re-layout is to avoid covering the content with the keyboard. The default value is true class ScffoldHomePage extends StatefulWidget { @override State<StatefulWidget> createState() { return ScffoldHomePageState(); } } 68
class ScffoldHomePageState extends State<ScffoldHomePage> { num index =0; List <Widget> pageWidgetList =[ Home(), SearchScreen(), ProfileScreen(), ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("HomePage"), backgroundColor: Colors.pink, ), body:pageWidgetList[index], floatingActionButton: FloatingActionButton( child: Text("++"), onPressed: () { }, tooltip: "Click tooltips", backgroundColor: Colors.pink, focusColor: Colors.green, hoverColor: Colors.purpleAccent, 69
splashColor: Colors.deepPurple, foregroundColor: Colors.white, elevation: 0.0, highlightElevation: 20.0, ), floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, persistentFooterButtons: <Widget>[ Text( "1", style: TextStyle(color: Colors.blue), ), Text("2"), Text("3"), Text("4"), Text("5"), ], drawer: Container( color: Colors.grey, width: 120, child: FlatButton( child: Text("Close Left Swipe"), onPressed: () { Navigator.of(context).pop(); }, ), ), endDrawer: Container( color: Colors.orange, width: 200, 70
height: 800, child: FlatButton( child: Text("Close Right Swipe",style: TextStyle(color: Colors.white),), onPressed: () { Navigator.of(context).pop(); }, ), ), bottomNavigationBar:new BottomNavigationBar( backgroundColor: Colors.pink, items: <BottomNavigationBarItem>[ BottomNavigationBarItem(icon:Icon(Icons.home,color: index==0?Colors.white:Colors.white,),title: Text("Home",style: TextStyle(color: index==0?Colors.white:Colors.white),) ), BottomNavigationBarItem(icon:Icon(Icons.search,color: index==1?Colors.white:Colors.white,),title: Text("Search",style: TextStyle(color: index==1?Colors.white:Colors.white),) ), BottomNavigationBarItem(icon:Icon(Icons.people,color: index==2?Colors.white:Colors.white,),title: Text("Account",style: TextStyle(color: index==2?Colors.white:Colors.white),) ), ], onTap: (flag) { print("flag $flag"); index = flag; setState(() {}); }, currentIndex: index, ) , ); } } 71
72
Dialogs A material design dialog, Dialogs are temporary windows that appear as overlays over the existing application Show dialog is simple showDialog(context: context, builder: (context) => Center(child: Text("Dialog"))); In flutter we have multiple ways to show dialogs 1. Alert Dialog 2. Custom Dialog 3. Full-Screen Dialog Simple AlertDialog showDialog( context: context, builder: (BuildContext context){ return AlertDialog( title: Text("Alert Dialog"), content: Text("Dialog Content"), ); } ) This is a simple alert dialog with title and message. We can also add buttons to handle the events 73
Add buttons to Alert Dialogs This widget, there is a parameter called action. It accepts an array of widgets and we can provide multiple buttons to that. Those Buttons will appear in the bottom right corner of the dialog actions:[ FlatButton( child: Text("Close"), ) ] How to close Dialog We can close the Displayed Dialog by calling the Navigator.of(context).pop(); FlatButton( child: Text("Close"), onPressed: (){ Navigator.of(context).pop(); }, ) Example class MyDialog extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Dialog"),backgroundColor: Colors.pink,), body: Center( 74
child: FlatButton( onPressed: () { showMyDialog(context); }, child: Text( 'Show Me', style: TextStyle( fontSize: 16.0, fontWeight: FontWeight.bold, color: Colors.white), ), color: Colors.pink, ), ), ); } void showMyDialog(BuildContext context) { showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return Dialog( child: _contentWidget(context), insetAnimationCurve: Curves.fastOutSlowIn, insetAnimationDuration: Duration(milliseconds: 100), shape: RoundedRectangleBorder( borderRadius: BorderRadius.all( Radius.circular(8.0), ), ),); }); } Widget _contentWidget(BuildContext context) { return Center( widthFactor: 2.0, heightFactor: 1.0, child: Container( width: 300.0, height: 200.0, 75
color: Colors.white, child: Column( children: <Widget>[ Expanded( child: Container( padding: EdgeInsets.only(top: 5.0), child: Text('This is title',style: TextStyle(color: Colors.black,fontWeight: FontWeight.bold,fontSize: 22.0),),), flex: 1, ), Expanded( child: Container( alignment: Alignment.topLeft, margin: EdgeInsets.all(20.0), child: Text('Text Message to display the Dialog in Flutter',style: TextStyle(fontSize: 18.0,color: Colors.black),), ), flex: 3, ), Expanded( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ RaisedButton(onPressed: (){ Navigator.of(context).pop(); }, child: Text('Continue',style: TextStyle(color: Colors.white)),color: Colors.pink,), FlatButton(onPressed: (){ Navigator.of(context).pop(); }, child: Text('Cancel',style: TextStyle(color: Colors.pink),)), ], ), flex: 2, ), ], 76
), ), ); } } 77
ExpansionPanelList & ExpansionPanel These two widgets are designed to work together to present a list of expandable panels to the user We have to manage the state of what was expanded / collapsed and rebuild the ExpansionPanelList & ExpansionPanels everytime the state changes ExpansionPanel Shrink the panel. It has a title and a body that can be expanded or collapsed. The body of the panel is only visible when expanded. The shrink panel is only used as a child of ExpansionPanelList. Example implementation, please use ExpansionPanelList ExpansionPanel({ @required this.headerBuilder, @required this.body, this.isExpanded = false, this.canTapOnHeader = false, }) ExpansionPanelList A material expansion panel list that lays out its children and animates expansions Lays out the child ExpansionPanels const ExpansionPanelList({ Key key, this.children = const <ExpansionPanel>[], this.expansionCallback, this.animationDuration = kThemeAnimationDuration, }) 78
There are only three parameters that we need to use: children: Needless to say, it is ExpansionPanel expansionCallback: expansion callback, here will return the index of the click animationDuration: the duration of the animation Example: class ExpansionWidget extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return ExpansionWidgetState(); } } class ExpansionWidgetState extends State<ExpansionWidget>{ List<bool>listExpans=List(); @override void initState() { // TODO: implement initState super.initState(); listExpans.add(false); listExpans.add(false); } @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( backgroundColor: Colors.grey, appBar: AppBar(title: Text("Expansionpanel"),backgroundColor: Colors.pink,), body: SingleChildScrollView( child: Container( 79
alignment: Alignment.center, child: Column( children: <Widget>[ ExpansionPanelList( children : <ExpansionPanel>[ ExpansionPanel( headerBuilder:(context, isExpanded){ return ListTile( title: Text('Try Expansion 1'), ); }, body: Padding( padding: EdgeInsets.fromLTRB(15, 0, 15, 15), child: ListBody( children: <Widget>[ Card( margin:EdgeInsets.fromLTRB(0, 0, 0, 10), child: Padding(padding: EdgeInsets.all(8),child: Text('Content 1'),), ), Card( margin:EdgeInsets.fromLTRB(0, 0, 0, 10), child: Padding(padding: EdgeInsets.all(8),child: Text('Content 2'),), ), Card( margin:EdgeInsets.fromLTRB(0, 0, 0, 10), child: Padding(padding: EdgeInsets.all(8),child: Text('Content 3'),), ), Card( margin:EdgeInsets.fromLTRB(0, 0, 0, 10), child: Padding(padding: EdgeInsets.all(8),child: Text('Content 4'),), ), Card( margin:EdgeInsets.fromLTRB(0, 0, 0, 10), 80
child: Padding(padding: EdgeInsets.all(8),child: Text('Content 5'),), ), ], ), ), isExpanded: listExpans[0], canTapOnHeader: true, ), ExpansionPanel( headerBuilder:(context, isExpanded){ return ListTile( title: Text('Try Expansion 2 '), ); }, body: Padding( padding: EdgeInsets.fromLTRB(15, 0, 15, 15), child: ListBody( children: <Widget>[ Card( margin:EdgeInsets.fromLTRB(0, 0, 0, 10), child: Padding(padding: EdgeInsets.all(8),child: Text('Content 1'),), ), Card( margin:EdgeInsets.fromLTRB(0, 0, 0, 10), child: Padding(padding: EdgeInsets.all(8),child: Text('Content 2'),), ), Card( margin:EdgeInsets.fromLTRB(0, 0, 0, 10), child: Padding(padding: EdgeInsets.all(8),child: Text('Content 3'),), ), Card( margin:EdgeInsets.fromLTRB(0, 0, 0, 10), 81
child: Padding(padding: EdgeInsets.all(8),child: Text('Content 4'),), ), Card( margin:EdgeInsets.fromLTRB(0, 0, 0, 10), child: Padding(padding: EdgeInsets.all(8),child: Text('Content 5'),), ), ], ), ), isExpanded: listExpans[1], canTapOnHeader: true, ), ], expansionCallback:(panelIndex, isExpanded){ setState(() { listExpans[panelIndex] = !isExpanded; }); }, animationDuration : kThemeAnimationDuration, ), ], ), ), ) ); } } 82
GridView GridView displays the values of a data source in a table where each column represents a field and each row represents a record Constructors GridView.builder() GridView.count() GridView.custom() GridView.extent() 83
GridView.builder({ Key key, Axis scrollDirection = Axis.vertical, bool reverse = false, ScrollController controller, bool primary, ScrollPhysics physics, bool shrinkWrap = false, EdgeInsetsGeometry padding, @required this.gridDelegate, @required IndexedWidgetBuilder itemBuilder, int itemCount, bool addAutomaticKeepAlives = true, bool addRepaintBoundaries = true, bool addSemanticIndexes = true, int semanticChildCount, }) Example class GridViewWidget extends StatelessWidget { List list = new List(); GridViewWidget() { for (int i = 1; i < 20; i++) { int j = (i % 9) + 1; var temp = { "imageurl": "https://cdn.pixabay.com/photo/2016/10/17/17/41/priyan ka-chopra-1748248_960_720.jpg", "title": "Image $i" }; list.add(temp); } } @override Widget build(BuildContext context) { 84
// TODO: implement build return Scaffold( appBar: AppBar( title: Text("GridView"), backgroundColor: Colors.pink, ), body: GridView.builder( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, mainAxisSpacing: 5, crossAxisSpacing: 5, ), itemCount: list.length, itemBuilder: (BuildContext context, int index) { return Container( decoration: BoxDecoration( border: Border.all( color: Colors.red, width: 2, )), child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[ Image.network( list[index]['imageurl'], fit: BoxFit.cover, ), Expanded(child: Text(list[index]['title'])), ], ), ); })); } } 85
PopupMenu Why use this PopupMenu? Because most message interfaces have a setting option in the upper right corner, and this option is most commonly implemented through PopupMenu. Look at the effect map 86
class PopupWidget extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return PopupWidgetState(); } } class PopupWidgetState extends State<PopupWidget> { int _value=1; @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text("Popup Window"),backgroundColor: Colors.pink, actions: <Widget>[ _NomalPopMenu() ],), body: Container( child: Center( child: Container( decoration:ShapeDecoration(shape: OutlineInputBorder( )), width: 200, height: 40, child: Center(child: Text("Value selected $_value",style: TextStyle(color: Colors.pink,fontSize: 20),)), ), ), ), ); 87
} Widget _NomalPopMenu() { return new PopupMenuButton<int>( itemBuilder: (BuildContext context) => <PopupMenuItem<int>>[ new PopupMenuItem<int>( value: 1, child: new Text('Item One')), new PopupMenuItem<int>( value: 2, child: new Text('Item Two')), new PopupMenuItem<int>( value: 3, child: new Text('Item Three')), new PopupMenuItem<int>( value: 4, child: new Text('I am Item Four')) ], onSelected: (int value) { setState(() { _value = value; }); }); } } Checked Widgets CheckBox Checkbox is a checkbox component, usually used for setting options Attributes ● activeColor : Color -the color when active. ● onChanged : ValueChanged -fired when changed. ● tristate: bool -If true, the value of the checkbox can be true, false or null. 88
● value : bool -the value of the checkbox CheckboxListTile CheckboxListTile is an upper-level package of Checkbox. Its appearance is to provide a selection component similar to a setting page, which can set icons and text Radio Radio is used for single selection options Attributes ● Value of value radio ● groupValue radio The value of the group. Value == groupValue is selected. ● onChanged callback when the value changes ● activeColor when selected Example class CheckWidgets extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return CheckWidgetState(); } } class Course{ Course(this.title,this.price,this.courseCheck); 89
bool courseCheck; String title; double price; } class CheckWidgetState extends State<CheckWidgets> { int priceCheck=0; String doller_rupee="Rs"; List<Course>listCourse=List(); @override void initState() { // TODO: implement initState super.initState(); listCourse.add(Course("Course 1",699,false)); listCourse.add(Course("Course 2",693,false)); } @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text("Checked Widgets"),backgroundColor: Colors.pink,), body: Container( margin: EdgeInsets.all(10), child: Column( children: <Widget>[ Card( child: Padding( padding: const EdgeInsets.all(8.0), child: Column( children: <Widget>[ Row( 90
children: <Widget>[ Text("Price in ",style: TextStyle(color: Colors.pink,fontSize: 22),), ], ), Row( children: <Widget>[ Row( children: <Widget>[ Radio( value: 0, activeColor: Colors.pink, groupValue: priceCheck, onChanged: (newValue) { setState(() { priceCheck = newValue; doller_rupee="Rs"; }); } ), Text('Rupee') ], ), Row( children: <Widget>[ Radio( value: 1, activeColor: Colors.pink, groupValue: priceCheck, onChanged: (newValue) { setState(() { priceCheck = newValue; doller_rupee="$"; }); } ), Text('Dollar') 91
], ), ], ), ], ), ), ) , Card( child: Padding( padding: const EdgeInsets.all(8.0), child: Column( children: <Widget>[ Row( children: <Widget>[ Text("Select Course",style: TextStyle(color: Colors.pink,fontSize: 24),), ], ), ListView.builder( shrinkWrap: true, itemCount: listCourse.length, itemBuilder: (ctx,pos){ return Column( children: <Widget>[ CheckboxListTile( subtitle: Text(" $doller_rupee ${listCourse[pos].price}",style: TextStyle(fontSize: 16,color: Colors.green),), title: Text(listCourse[pos].title,style: TextStyle(fontSize: 22,color: Colors.black),), checkColor:Colors.white, activeColor:Colors.pink, value: this.listCourse[pos].courseCheck, onChanged: (bool value) { setState(() { listCourse[pos].courseCheck = 92
!listCourse[pos].courseCheck; }); }, ), Divider(height: 2,color: Colors.pink,) ], ); }) ], ), ), ),], ), ), ); } } 93
94
TabBar TabBarView TabBar is a row of horizontal tabs, you can switch back and forth, the effect map TabBar is generally used together with TabBarView. TabBarView is used to select different TabBars. TabBarView displays the corresponding View TabBarView property description class TabBarDemo extends StatefulWidget { @override State<StatefulWidget> createState() => _TabBar(); } class _TabBar extends State<TabBarDemo> { final List<String> _tabValues = [ 'Tab1', 'Tab2', 'Tab3', 'Tab4', 'Tab5', 'Tab6', 'Tab7', 'Tab8', ]; TabController _controller; @override void initState() { super.initState(); _controller = TabController( length: _tabValues.length, 95
vsync: ScrollableState(), ); } @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar( backgroundColor: Colors.pink, title: Text('TabBar'), bottom: TabBar( tabs: _tabValues.map((f) { return Text(f,style: TextStyle(fontSize: 20),); }).toList(), controller: _controller, indicatorColor: Colors.grey, isScrollable: true, labelColor: Colors.white, unselectedLabelColor: Colors.white54, indicatorWeight: 5.0, labelStyle: TextStyle(height: 2), ), ), body: TabBarView( controller: _controller, children: _tabValues.map((f) { return Center( child: Text(f,style: TextStyle(fontSize: 80,color: Colors.pink),), ); }).toList(), ), ); } } 96
Table A widget that uses the table layout algorithm for its children The height of each row of the table is determined by its content, and the width of each column is controlled individually by the columnWidths property 97
class TableWidget extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text("Table Widget"),backgroundColor: Colors.pink,), body: Container( child: Padding( padding: const EdgeInsets.all(8.0), child: Card( child: Table( columnWidths: const <int, TableColumnWidth>{ 0: FixedColumnWidth(50.0), 1: FixedColumnWidth(100.0), 2: FixedColumnWidth(50.0), 3: FixedColumnWidth(100.0), }, border: TableBorder.all(color: Colors.pink, width: 1.0, style: BorderStyle.solid), children: const <TableRow>[ TableRow( children: <Widget>[ Center(child: Text('A1',style: TextStyle(fontSize: 18),)), Center(child: Text('B1',style: TextStyle(fontSize: 18),)), Center(child: Text('C1',style: TextStyle(fontSize: 18),)), Center(child: Text('D1',style: TextStyle(fontSize: 18),)), ], ), TableRow( children: <Widget>[ Center(child: Text('A2',style: TextStyle(fontSize: 18),)), Center(child: Text('B2',style: TextStyle(fontSize: 18),)), Center(child: Text('C2',style: TextStyle(fontSize: 18),)), Center(child: Text('D2',style: TextStyle(fontSize: 18),)), ], 98
), TableRow( children: <Widget>[ Center(child: Text('A3',style: TextStyle(fontSize: 18),)), Center(child: Text('B3',style: TextStyle(fontSize: 18),)), Center(child: Text('C3',style: TextStyle(fontSize: 18),)), Center(child: Text('D3',style: TextStyle(fontSize: 18),)), ], ), ], ), ), ) , ), ); } } 99
100
Future and FutureBuilder To Make the Asynchronous operation will use Future Uses future.then get future value and catch future exception Combined async,await future.whenComplete future.timeout import 'dart:async'; Future<String> testFuture() { // throw new Error(); return Future.value('success'); // return Future.error('error'); } main() { testFuture().then((s) { print(s); }, onError: (e) { print('onError:'); print(e); }).catchError((e) { print('catchError:'); print(e); }); } Sometimes we need to Futuredo something at the end, and we know then().catchError()the pattern is similar try-catch, try-catch there is a finally code block, and future.whenCompletethat Future Is finally 101
FutureBuilder FutureBuilder combines asynchronous operations and asynchronous UI updates. Through it we can update the results of network requests, database reads, etc. future: Future object represents the asynchronous calculation currently connected to this builder; initialData: Indicates the initialization data of a non-empty Future before completion; builderA return function of type AsyncWidgetBuilder is a function that builds a widget based on asynchronous interaction Properties connectionState -Enumeration of ConnectionState, which indicates the connection status with asynchronous calculation. ConnectionState has four values: none, waiting, active, and done; data -Asynchronous calculation of the latest data received; error -Asynchronously calculate the latest error object received; Example class FutureWidget extends StatefulWidget { String computeListOfTimestamps(int count) { StringBuffer sb = new StringBuffer(); var random = Random(); for (int i = 0; i < 2000; i++) { sb.writeln("Random Number : ${random.nextInt(2000)}"); sb.writeln(""); } return sb.toString(); } 102
Future<String> createFutureCalculation(int count) { return new Future(() { return computeListOfTimestamps(count); },); } @override FutureWidgetState createState() => new FutureWidgetState(); } class FutureWidgetState extends State<FutureWidget> { bool _showCalculation = false; bool isLoading = false; GlobalKey<ScaffoldState>_scaffoldKey=GlobalKey(); void _onInvokeFuturePressed() { setState(() { _showCalculation = !_showCalculation; isLoading=true; }); } @override Widget build(BuildContext context) { Widget child = _showCalculation ? FutureBuilder( future: widget.createFutureCalculation(1000), builder: (BuildContext context, AsyncSnapshot snapshot) { return Expanded( child: SingleChildScrollView( child: (snapshot.data == null)?Center(child: CircularProgressIndicator()):Text( '${snapshot.data == null ? "" : snapshot.data}', 103
style: TextStyle(fontSize: 20.0)))); }) : Text('Press Refresh to load Data'); return new Scaffold( key: _scaffoldKey, appBar: new AppBar( backgroundColor: Colors.pink, title: new Text("Future Builder "), ), body: new Center( child: new Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[child])), floatingActionButton: new FloatingActionButton( backgroundColor: Colors.pink, onPressed: _onInvokeFuturePressed, tooltip: 'Call Future', child: new Icon(Icons.refresh), ), // This trailing comma makes auto-formatting nicer for build ); } } 104
StreamBuilder StreamBuilder is similar to FutureBuilder, provides the ability to acquire asynchronous data and update ui Stream is an event stream, which is similar to RxJava. It allows us to emit an event from one end and listen to changes in the event from the other 105
end. Through Stream we can design reactive code based on event flow on Flutter logic Stream is not flutter widget, it provided by Dart Stream is an abstract interface StreamBuilder Constructor const StreamBuilder({ Key key, this.initialData, Stream<T> stream, @required this.builder, }) Example class StreamBuilderWidget extends StatelessWidget{ var index = 0; StreamSubscription<String> subscription; var streamController = StreamController<String>(); //To Emit the stream StreamSink<String> get streamSink => streamController.sink; Stream<String> get streamData => streamController.stream; StreamBuilderWidgetState() { streamSink.add("0"); } 106
void onFloatActionButtonPress() { streamSink.add(index.toString()); index++; } @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text("Stream Builder"),backgroundColor: Colors.pink,), body: Center( child: StreamBuilder<String>( stream: streamData, builder: (BuildContext context, AsyncSnapshot<String> snapshot) { return Text('Result: ${snapshot.data}'); } ) ), floatingActionButton: FloatingActionButton( onPressed: onFloatActionButtonPress, child: Icon(Icons.add)) ); } } 107
Navigation To Implement page navigation in Flutter we need to use two classes Navigator and Route. The Navigator is responsible for the stack structure processing of the page and the route processing of the Route complex page The Navigator used the stack structure to manage pages. When a page needs to be added, the stack method is used, when we need to exit a page use the pop-out method Push and pop will handle all stack elements Let’s Start Code Create Two Pages First, we will create two pages, each of which contains a button. Clicking the button on the first page will jump to the second page, and clicking the button on the second page will return to the first page class FirstPage extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text(" First Page"), backgroundColor: Colors.pink,), body: Center( child: RaisedButton( onPressed: (){ 108
}, child: Text("Second Page", style: TextStyle(color: Colors.white),), color: Colors.pink, ), ), ); } } class SeconPage extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text(" Second Page"), backgroundColor: Colors.pink,), body: Center( child: RaisedButton( onPressed: (){ }, child: Text("Navigate to First Page", style: TextStyle(color: Colors.white),), color: Colors.pink, ), ), ); } } 109
Now let’s jump to second page In order to jump to the second page, we will use the Navigator.push method. This push method will add one Route to the routing heap managed by Navigator. But the Push method requires one Route, but Route where does it come from? We can create our own Route, or use it MaterialPageRoute. MaterialPageRoute Conveniently, it will jump to a new page using platform-specific animations. In the FirstScreen Widget build method, update the onPressedcallback onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (context) => SeconPage()), ); }, Return to the first Now that we are on the second screen, how do we close it and return to the first screen? Use the Navigator.pop method! pop Method to remove the current one from the routing stack managed by Navigator Route. In this part, update SecondScreenthe onPressedcallback in the widget onPressed: () { Navigator.pop(context); }, 110
Complete code class NavigationDemo extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return FirstPage(); } } class FirstPage extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text(" First Page"), backgroundColor: Colors.pink,), body: Center( child: RaisedButton( onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (context) => SeconPage()), ); }, child: Text("Second Page", style: TextStyle(color: Colors.white),), color: Colors.pink, ), ), ); } } class SeconPage extends StatelessWidget{ @override 111
Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text(" Second Page"), backgroundColor: Colors.pink,), body: Center( child: RaisedButton( onPressed: () { Navigator.pop(context); }, child: Text("Navigate to First Page", style: TextStyle(color: Colors.white),), color: Colors.pink, ), ), ); } } 112
Routing table (Named Routes) Flutter also provided Navigation with NamedRoutes The simple way is to define all routes in the runApp, so that centralized management is possible, which is also a very recommended approach A MaterialApp is the easiest way to set it up, and the MaterialApp's home becomes the route at the bottom of the navigator stack. To push a new route on the stack, you can create a MaterialPageRoute instance with builder capabilities class NamedRoutes extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build 113
return MaterialApp( home: HomePage(), routes: <String,WidgetBuilder>{ '/f':(BuildContext ctx)=> FisrtPage(), '/s':(BuildContext ctx)=> SecondPage(), '/t':(BuildContext ctx)=> ThirdPage(), }, ); } } class HomePage extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text("HomePage"),backgroundColor: Colors.pink,), body: Center( child: Column( mainAxisSize: MainAxisSize.min, children: <Widget>[ RaisedButton( color: Colors.pink, onPressed: (){ Navigator.pushNamed(context, "/f"); },child: Text("FirstPage",style: TextStyle(color: Colors.white)),), RaisedButton( color: Colors.pink, onPressed: (){ Navigator.pushNamed(context, "/s"); },child: Text("SecondPage",style: TextStyle(color: Colors.white)),), 114
RaisedButton( color: Colors.pink, onPressed: (){ Navigator.pushNamed(context, "/t"); },child: Text("ThirdPage",style: TextStyle(color: Colors.white),),), ], ), ), ); } } class FisrtPage extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text("FirstPage"),backgroundColor: Colors.pink,), body: Center( child: Column( children: <Widget>[ RaisedButton(onPressed: (){Navigator.pop(context,"From First page");},child: Text("FirstPage"),) ], ), ), ); } } class SecondPage extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build 115
return Scaffold( appBar: AppBar(title: Text("SecondPage"),backgroundColor: Colors.pink,), body: Center( child: Column( children: <Widget>[ RaisedButton(onPressed: (){Navigator.pop(context);},child: Text("SecondPage"),) ], ), ), ); } } class ThirdPage extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text("ThirdPage"),backgroundColor: Colors.pink,), body: Center( child: Column( children: <Widget>[ RaisedButton(onPressed: (){Navigator.pop(context);},child: Text("ThirdPage"),) ], ), ), ); } } To get the result from called page by 116
onPressed: (){ Navigator.pushNamed(context, "/f").then((value){ print(value); }); } After pop the screen the then() method will execute and print the result. Navigation switch animation The default navigation switch animation is a pop-up process from bottom to top on Android, and a pan-to-left process from iOS In order to unify, you can customize a route, including the color and behavior of the modal barrier of the route, and the animation conversion of other aspects of the route You can find the example of Navigation Screen animation at http://rrtutors.com/description/53 117
Form & Form Fields A form is an area that contains form elements. Form elements allow users to enter content, such as text fields, drop-down lists, radio boxes, check boxes, and so on. Common application scenarios are: login, registration, input information, etc. There are two important components in the form, one is the Form component for the entire form submission, and the other is the TextFormField component for user input Form Constructor const Form({ Key key, @required this.child, this.autovalidate = false, this.onWillPop, this.onChanged, }) The Form object gives the following methods: reset to reset fields. save to save fields. validate to validate, returning a true if the form fields are valid, false if one or more are invalid Form State The Form object stores input state data from child TextFormFields but not other field types like Checkboxes, DropdownButtons, Radios, Switches. So, 118
if we want form to work with those other types of fields, we need to store the state of those items. If we take a look a look at the example we will see that these fields are stored as state in the Stateful Widget Form Validation As mentioned earlier, the Form class has an autovalidate constructor argument. If this argument is set to true, the framework invokes validation as data is input. If this argument is set to false, the framework will not invoke validation until the validate method is invoked Example With Login Page class FormWidget extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return _LoginPageState(); } } class _LoginPageState extends State<FormWidget> { GlobalKey<FormState> loginKey = GlobalKey<FormState>(); String userName; String password; void login() { 119
var loginForm = loginKey.currentState; if (loginForm.validate()) { loginForm.save(); print('userName:' + userName + ',password:' + password); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Form Fileds'), backgroundColor: Colors.pink, centerTitle: true, ), body: SingleChildScrollView( child: Column( children: <Widget>[ Container( padding: EdgeInsets.all(16), child: Form( key: loginKey, autovalidate: true, child: Column( children: <Widget>[ TextFormField( decoration: InputDecoration( labelText: 'User Name', hintText: "Enter User Name", hintStyle: TextStyle( color: Colors.grey, fontSize: 13, ), prefixIcon: Icon(Icons.person,color: Colors.pink,), 120
), validator: (value) { return value.trim().length > 0 ? null : "Please enter valid user name"; }, onSaved: (value) { userName = value; }, onFieldSubmitted: (value) {}, ), TextFormField( decoration: InputDecoration( labelText: 'Password', hintText: 'Enter password', hintStyle: TextStyle( color: Colors.grey, fontSize: 13, ), prefixIcon: Icon(Icons.lock,color: Colors.pink), ), obscureText: true, validator: (value) { return value.length < 6 ? 'Password should be min 6 characters' : null; }, onSaved: (value) { password = value; }, ), ], ), 121
onChanged: () { print("onChanged"); }, ), ), Container( padding: EdgeInsets.all(16), child: Row( children: <Widget>[ Expanded( child: RaisedButton( padding: EdgeInsets.all(15), child: Text( "Login", style: TextStyle(fontSize: 18), ), textColor: Colors.white, color: Colors.pink, onPressed: login, ), ), ], ), ) ], ), ), ); } } 122
Input Decoration Themes By using this Input Decoration themes we can change the style for form fields as we like ThemeData( scaffoldBackgroundColor: Color(0xFF000000), appBarTheme: AppBarTheme( color: Colors.white, textTheme: TextTheme( title: TextStyle(fontSize: 22, color: Colors.white), ) 123
), brightness: Brightness.dark, backgroundColor: Color(0xFF000000), accentColor: Colors.white, accentIconTheme: IconThemeData(color: Colors.black), dividerColor: Colors.black54, accentTextTheme:TextTheme( headline: TextStyle(fontSize: 35, color: Colors.white), title: TextStyle(fontSize: 35, color: Colors.white), body1: TextStyle(fontSize: 35, color: Colors.white), subtitle: TextStyle(fontSize: 18, color: Colors.white), display1: TextStyle(fontSize: 35, color: Colors.white) ), inputDecorationTheme: InputDecorationTheme( focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.white), borderRadius: BorderRadius.all(Radius.circular(10))), enabledBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.white), borderRadius: BorderRadius.all(Radius.circular(10))), errorBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.white), borderRadius: BorderRadius.all(Radius.circular(10))), focusedErrorBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.white), borderRadius: BorderRadius.all(Radius.circular(10))), border: OutlineInputBorder( borderSide: BorderSide(color: Colors.white), borderRadius: BorderRadius.all(Radius.circular(10)), ), labelStyle: TextStyle( color: Colors.white, fontSize: 12.0 ), ), buttonTheme: ButtonThemeData( shape: new RoundedRectangleBorder( 124
borderRadius: BorderRadius.all(Radius.circular(10.0)), side: BorderSide(color: Colors.white)), ) ); We can change the Theme of application dynamically, you can find sample at http://rrtutors.com/description/27 125
Networking & JSON and Serialization Now a days every mobile application communicate with remote server on Networking Flutter provides a sky_enginedevelopment kit in the Flutter engine , which contains a _http library that contains various operation classes related to encapsulated http requests. In this article, we will introduce _http the use of related operation classes and the use _httpof three-party dio network libraries based on encapsulation HTTP Introduction The Hypertext Transfer Protocol (HTTP) is designed to enable communications between clients and servers. HTTP works as a request-response protocol between a client and server. A protocol describes how machines communicate with each other using messages. A protocol defines the format of these messages HTTPClient Make HTTPRequest http support is located dart:io, so to create an HTTP client, we need to add an import import 'dart:io'; var httpClient = new HttpClient(); 126
HTTP API uses Dart Futures in the return value . We recommend using the async/ await syntax to call the AP Involve below steps to handle the HttpClient ● Create client ● Construct Uri ● Make a request, wait for a request, and you can also configure request headers and body ● Close the request and wait for a response ● Decode the content of the response get() async { var httpClient = new HttpClient(); var uri = new Uri.http( 'domain', 'path to api', params in map object); var request = await httpClient.getUrl(uri); var response = await request.close(); var responseBody = await response.transform(UTF8.decoder).join(); } Decode and encode JSON The dart:convertlibrary makes it easy to decode and encode JSON. Decode a simple JSON string and parse the response into a Map Map data = JSON.decode(responseBody); String name=mapNews[0]['name'] 127
Check Example class NetwotkHTTP extends StatefulWidget { NetwotkHTTP({Key key}) : super(key: key); @override _NetwotkHTTPState createState() => new _NetwotkHTTPState(); } class _NetwotkHTTPState extends State<NetwotkHTTP> { Map<int,dynamic>mapNews=Map(); @override void initState() { // TODO: implement initState super.initState(); _getNewsData(); } _getNewsData() async { var url='https://newsapi.org/v2/sources?apiKey=API_KEY&p age=1'; //var url = 'https://httpbin.org/ip'; var httpClient = new HttpClient(); var listNewsdata=""; try { var request = await httpClient.getUrl(Uri.parse(url)); var response = await request.close(); if (response.statusCode == HttpStatus.OK) { var json = await response.transform(utf8.decoder).join(); var data = jsonDecode(json); 128
List<dynamic>hm=data['sources'] as List; setState(() { mapNews=hm.asMap(); print("errorr SetState"+mapNews.toString()); }); } else { print("errorr "); listNewsdata = 'Error Resposne :nHttp status ${response.statusCode}'; } } catch (exception) { print("errorr $exception"); listNewsdata = 'Failed getting News Data $exception'; } // If the widget was removed from the tree while the message was in flight, // we want to discard the reply rather than calling setState to update our // non-existent appearance. if (!mounted) return; } @override Widget build(BuildContext context) { var spacer = new SizedBox(height: 32.0); return new Scaffold( appBar: AppBar(title: Text("News API WITH HTTP CLIENT"),backgroundColor: Colors.pink,), body: SingleChildScrollView( 129
child: new Center( child: new Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ (mapNews.length==0)?CircularProgressIndicator():Text(""), ListView.builder( shrinkWrap: true, primary: false, itemCount: mapNews.length, itemBuilder: (ctx,pos){ return Card( elevation: 5, child: Container( margin: EdgeInsets.all(5), padding: EdgeInsets.all(5), child: InkWell( onTap: (){}, child: Row( children: <Widget>[ Container( height: 50, width: 50, decoration: ShapeDecoration(shape: CircleBorder(),color: Colors.pink), child: Center(child: Text(mapNews[pos]['name'].substring(0,1).toUpperCase(),style: TextStyle(fontSize: 30,color: Colors.white),)), ), SizedBox(width:15,), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Text(mapNews[pos]['name'],style: TextStyle(fontSize: 18,color: Colors.black),), SizedBox(height:5,), Text(mapNews[pos]['description'],style: 130
TextStyle(fontSize: 12,color: Colors.grey[800]),maxLines: 3,), ], ), ) ], ), ), ) ); }) ], ), ), ), ); } } HTTP Library http Library http.dartfile package HttpClientclass is our common network requests the operation class, which is an abstract class, the specific operation by the http_impl.dart document _HttpClient class that implements the class encapsulates http request various methods, including get, post, put, delete, patchAnd head wait for the request Let's look at the usage method through the following example 131
In this example using the NewsAPI to fetch the news headlines. News API is a simple HTTP REST API for searching and retrieving live articles from all over the web To call api in Flutter we need to add http: ^0.12.0+2 dependencies in the pubspec.yaml file The above same example with http library static Future callNewsCAtegory() async{ var url="API_KEY"; return get('https://newsapi.org/v2/sources?apiKey=$url&page=1') ; } The above method will return the response data, that we are handle in below code fetchNews(){ var callNews = NewsCategoryModel.callNewsCAtegory(); callNews.then((data){ var response=json.decode(data.body ); print(response); var listNewsdata=response['sources']as List; setState(() { listNews=listNewsdata.map<NewsCategoryModel>((model) =>NewsCategoryModel.fromJson(model)).toList(); listNewsAll.clear(); listNewsAll.addAll(listNews); }); 132
},onError: (error){ print("Result Error $error"); } ); } POST Method Map<String, String> queryParameters = {'key': 'value', 'key': 'value'}; Map<String, String> header = {'key': 'value'}; post("API_URL",body: queryParameters,headers: header); JSON Parsing After getting the response from the API we need to parse the data, for this we are using the import 'dart:convert' library var callNews = NewsCategoryModel.callNewsCAtegory(); callNews.then((data){ var response=json.decode(data.body ); print(response); var listNewsdata=response['sources']as List Var listNews=listNewsdata.map<NewsCategoryModel>((model)=>Ne wsCategoryModel.fromJson(model)).toList(); The NewsCategoryModel class like below 133
class NewsCategoryModel { String id; String name; String description; String url; String category; String language; String country; NewsCategoryModel(this.id,this.name,this.description,this.u rl,this.category,this.language,this.country); NewsCategoryModel.fromJson(Map<String,dynamic>parseModel ) { id=parseModel['id']; name=parseModel['name']; description=parseModel['description']; url=parseModel['url']; category=parseModel['category']; language=parseModel['language']; country=parseModel['country']; } static Future callNewsCAtegory() async{ var url="API_KEY"; return get('https://newsapi.org/v2/sources?apiKey=$url&page=1') ; } } 134
CompleteExample class NewsCategory extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return NewsCategorystate(); } } class NewsCategorystate extends State<NewsCategory>{ static const _appPrimaryValue = Colors.pink; List<NewsCategoryModel>listNews; List<NewsCategoryModel>listNewsAll; Icon _searchIcon = new Icon(Icons.search); Widget _appBarTitle = new Text( 'Search by Category' ); TextEditingController searchController=new TextEditingController(); List<Color>liscolors=new List(); @override void initState() { // TODO: implement initState super.initState(); liscolors.add(Color(0xFF009688)); liscolors.add(Color(0xFFFF0080)); liscolors.add(Color(0xFF800080)); listNews=new List(); listNewsAll=new List(); //_searchPressed(); searchController.addListener((){ print(searchController.text); if (searchController.text.isEmpty) { setState(() { 135
listNews.clear(); listNews.addAll(listNewsAll); }); } else { setState(() { listNews.clear(); for(int k=0;k<listNewsAll.length;k++) { print(listNewsAll[k].name+" names "); if(listNewsAll[k].name.toLowerCase().contains(searchControl ler.text.toLowerCase())) { print(listNewsAll[k].name+" names inside"); listNews.add(listNewsAll[k]); } } }); } }); fetchNews(); } @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar( backgroundColor: _appPrimaryValue, title: _appBarTitle, leading: IconButton(icon: this._searchIcon, onPressed: (){ _searchPressed(); }), ), body:(listNews.length>0)?ListView.builder( itemCount: listNews.length, itemBuilder: (ctx,index){ return setNewsItem(listNews[index],index); 136
}):Center(child: CircularProgressIndicator(),), ); } setNewsItem(NewsCategoryModel newsModel,index) { Color color=Colors.green[700]; if(index==0||index==5||index==11) color=liscolors[0]; else if(index%2==0) color=liscolors[1]; else color=liscolors[2]; return Card( elevation: 5, child: Container( margin: EdgeInsets.all(5), padding: EdgeInsets.all(5), child: InkWell( onTap: (){ HashMap<String,String>hm=new HashMap(); hm['category']=newsModel.category; Navigator.pushNamed(context, "/news",arguments: hm); }, child: Row( children: <Widget>[ Container( height: 50, width: 50, decoration: ShapeDecoration(shape: CircleBorder(),color: color), child: Center(child: Text(newsModel.name.substring(0,1).toUpperCase(),style: TextStyle(fontSize: 30,color: Colors.white),)), ), SizedBox(width:15,), 137
Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Text(newsModel.name,style: TextStyle(fontSize: 18,color: Colors.black),), SizedBox(height:5,), Text(newsModel.description,style: TextStyle(fontSize: 12,color: Colors.grey[800]),maxLines: 3,), ], ), ) ],), ),), ); } fetchNews(){ var callNews = NewsCategoryModel.callNewsCAtegory(); callNews.then((data){ var response=json.decode(data.body ); print(response); var listNewsdata=response['sources']as List; setState(() { listNews=listNewsdata.map<NewsCategoryModel>((model)=>Ne wsCategoryModel.fromJson(model)).toList(); listNewsAll.clear(); listNewsAll.addAll(listNews); }); },onError: (error){ print("Result Error $error"); } ); 138
} void _searchPressed() { setState(() { if (this._searchIcon.icon == Icons.search) { this._searchIcon = new Icon(Icons.close); this._appBarTitle = new TextField( controller: searchController, cursorColor: Colors.white, style: TextStyle(color: Colors.white), decoration: new InputDecoration( hintStyle: TextStyle(color: Colors.white), prefixIcon: new Icon(Icons.search,color: Colors.white,), hintText: 'Search...', ), ); } else { this._searchIcon = new Icon(Icons.search); this._appBarTitle = new Text('Search Example'); searchController.clear(); } }); } } 139
140
Database and local storage In Android we have different ways to store data ● SharedPreferences ● Local database ● Files Similarly in Flutter also we can handle the data by above ways SharedPreferences Shared Preferences allow us to save and retrieve data in the form of key,value pair How to access Shared Preferences in Flutter? In Flutter, we can access this feature by using the plugin Shared_Preferences Example class MySharedPref extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return MySharedPrefState(); } } class MySharedPrefState extends State<MySharedPref>{ @override Widget build(BuildContext context) { 141
// TODO: implement build return Scaffold( appBar: AppBar(title: Text("SharedPreference"),backgroundColor: Colors.pink,), floatingActionButton: FloatingActionButton( backgroundColor: Colors.pink, child: Icon(Icons.add), onPressed: (){ _incrementCounter(); }), body: Center( child: FutureBuilder( future: _getIncrementCounter(), builder:(BuildContext context, AsyncSnapshot snapshot) { return Column( mainAxisSize: MainAxisSize.min, children: <Widget>[ Text("The Incremented Value is ",style: TextStyle(color: Colors.pink,fontSize: 20),), SizedBox(height: 20,), Text((snapshot.data==null)?"":snapshot.data.toString(),style: TextStyle(color: Colors.pink,fontSize: 20),), ], ); }, ), ), ); } _incrementCounter() async { SharedPreferences prefs = await SharedPreferences.getInstance(); int counter = (prefs.getInt('counter') ?? 0) + 1; setState(() { prefs.setInt('counter', counter); 142
}); } Future<int>_getIncrementCounter() async { SharedPreferences prefs = await SharedPreferences.getInstance(); int counter = (prefs.getInt('counter') ?? 0) + 1; return counter; } } 143
144
Database In Android, we can use SQLite to store structured data that is queried via SQL. How to access SQLite in Flutter? In Flutter, we can use the SQFlite plugin to access this feature of SQFlite SQFlite Features ● Supports transactions and batch operations ● Automatic version management during program opening ● Addition, deletion, change, and help program ● Perform DB operations in iOS and Android background threads SQFlite does not perform a type check on the value, that is, a column of type INTEGER can store TEXT, but when we parse the result of the query and map it, it will report a type exception. So still avoid storing inconsistent data SQFlite supports 5 data types: NULL , INTEGER , REAL , TEXT , BLOB NULL When a column does not store data, the default value is NULL. INTEGER Int type in dart, the value range is -2 ^ 63 to 2 ^ 63-1 REAL 145
num type in dart, namely int and double types TEXT String type in dart BLOB The Uint8List type in dart, although it can store List <int>, is not officially recommended because the conversion is slow bool Stores INTEGER type, 0 is false, 1 is true If we need to store other types of data, such as bool, DateTime, List <String>, etc., we need to handle it by ourselves. Everyone may have their own unique method. I hope you can make some suggestions. We can encapsulate entity classes and parsing classes. From the perspective of external code, these types of storage, such as bool, DateTime, and List <String>, are implemented DateTime Store INTEGER type, the creation time and update time of a column of data is generally more important. Of course, there are other information, such as the payment time, delivery time, and cancellation time of an order. If the TEXT type is stored, the program is inconvenient if it supports multiple languages. List To store TEXT type, we can combine the data into String and store it in the database according to the special separator. It is then parsed into a List <String> according to the split of the String. There are still many things to note, such as the elements of List must not contain the defined delimiters. It is troublesome to modify a certain Item of List, and it can only cover List as a whole. 146
Map, json, entity classes Store the TEXT type. Generally, I use the toMap method of the entity class to convert the entity class into a Map. The entity class is converted to a String through jsonEncode. In turn, the string is converted to a Map using jsonDecode, and the entity class is converted from the Map to the entity class Database operations Database creation Open the database based on the name and version number createDB(VERSION) async { String databasesPath = await getDatabasesPath(); // Database Path: /data/user/0/com.package.name/databases String path = join(databasesPath, 'db_name.db'); // Path: /data/user/0/com.package.name/databases/db_name.db Database database = await openDatabase( path, version: VERSION, onCreate: (Database db, int version) async { }, onUpgrade: (Database db, int oldVersion, int newVersion) async { }, ); } Create Table The Create Table query should be inside onCreate() method "CREATE TABLE users(id INTEGER PRIMARY KEY autoincrement, name TEXT, email TEXT, password TEXT, mobile TEXT)"; 147
Delete Table db.execute('DROP table users'); Clear Table db.execute('DELETE FROM users'); Rename Table db.execute('ALTER TABLE users RENAME TO users_1'); Add Field db.execute('ALTER TABLE users ADD gender TEXT'); Delete Field db.execute('ALTER TABLE users DROP COLUMN gender'); Modify Field Type db.execute('ALTER TABLE users ALTER COLUMN value integer'); Insert Insert will returns the last inserted record id int id = await database.rawInsert('INSERT INTO user(name, email, password, mobile) VALUES("shiva", "test@tes.com", "1234#","1234567899")'); Delete returns the number of records affected int count = await database.rawDelete('DELETE FROM user WHERE email = ?', ['test@test.com']); 148
Update int count = await database.update( 'user', {'name': 'Name 2'} where: 'email = ?', whereArgs: ['test@test.com'] Query Query is the most complicated one in SQL statements. The keywords include distinct, where, group by, having, count, order by asc / desc, limit, offset, in, join, as, union and so on List<Map<String, dynamic>> result = await database.query( 'user', distinct: true, columns: ['name','mobile'], where: 'email = ?', whereArgs: ['test@test.com'], groupBy: 'name', limit: 5, offset: 2, ); List<Map<String, dynamic>> result = await database.rawQuery( 'SELECT * FROM user WHERE email=test@test.com order by name asc limit 5 offset 2', [16], ); Example Add Plugins Add below plugins to pubspec.yaml file sqflite: path: path_provider: ^0.4.1 Create User Model class class User{ int id; String name; String email; 149
String pasword; String mobile; User(this.name,this.email,this.pasword,this.mobile); Map<String,dynamic>toUserMap(){ return { 'name':name, 'email':email, 'password':pasword, 'mobile':mobile, }; } static fromMap(Map<String, dynamic> c) { return User(c['name'],c['email'],c['passowrd'],c['mobile']); } } Create Database import 'dart:io'; import 'package:flutter_firebase_app/models/user.dart'; import 'package:path/path.dart'; import 'package:sqflite/sqflite.dart'; import 'package:path_provider/path_provider.dart'; class UserDatabase{ static String path; static final _databaseName = "mydb.db"; static final _databaseVersion = 1; static final _table_user = 'users'; static final _table_logins = 'logins'; UserDatabase._privateConstructor(); static final UserDatabase instance = UserDatabase._privateConstructor(); // only have a single app-wide reference to the database static Database _database; Future<Database> get database async { if (_database != null) return _database; // lazily instantiate the db the first time it is accessed _database = await _initDatabase(); return _database; } // this opens the database (and creates it if it doesn't exist) _initDatabase() async { Directory documentsDirectory = await getApplicationDocumentsDirectory(); String path = join(documentsDirectory.path, _databaseName); 150
return await openDatabase(path, version: _databaseVersion, onCreate: _onCreate); } // SQL code to create the database table Future _onCreate(Database db, int version) async { await db.execute( "CREATE TABLE users(id INTEGER PRIMARY KEY autoincrement, name TEXT, email TEXT, password TEXT, mobile TEXT)", ); await db.execute( "CREATE TABLE logins(name TEXT, email TEXT, mobile TEXT,password TEXT)", ); } static Future<String> getFileData() async { return getDatabasesPath().then((s){ return path=s; }); } Future<int> insertUser(User user) async{ Database db = await instance.database; var users=await db.rawQuery("select * from users where mobile = "+user.mobile); if(users.length>0) { return -1; } return await db.insert("users",user.toUserMap(),conflictAlgorithm: ConflictAlgorithm.ignore ); } Future<User> checkUserLogin(String mobile, String password) async { Database db = await instance.database; var res=await db.rawQuery("select * from users where mobile = '$mobile' and password = '$password'"); if(res.length>0) { List<dynamic> list = res.toList().map((c) => User.fromMap(c)).toList() ; print("Data "+list.toString()); await db.insert("logins",list[0].toUserMap()); return list[0]; } return null; } Future<int> getUser() async{ Database db = await instance.database; var logins=await db.rawQuery("select * from logins"); if(logins==null) 151
return 0; return logins.length; } Future<User> getUserData() async{ Database db = await instance.database; var res=await db.rawQuery("select * from logins"); print("result user data $res"); print("result user data "+res.toString()); List<dynamic> list = res.toList().map((c) => User.fromMap(c)).toList() ; return list[0]; } Future<int> deleteUser(String mobile) async{ Database db = await instance.database; var logins= db.delete(_table_logins, where: "mobile = ?", whereArgs: [mobile]); return logins; } } Signup Page import 'package:flutter/material.dart'; import 'package:flutter_firebase_app/databases/UserDatabase.dart'; import 'package:flutter_firebase_app/models/user.dart'; class SignupPage extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return SignUpState(); } } class SignUpState extends State<SignupPage>{ final _formKey = GlobalKey<FormState>(); final _scafoldKey = GlobalKey<ScaffoldState>(); final _nameEditController=TextEditingController(); final _emailEditController=TextEditingController(); final _mobileEditController=TextEditingController(); final _passwordEditController=TextEditingController(); String email_pattern = r'^(([^<>()[].,;:s@"]+(.[^<>()[].,;:s@"]+)*)|(".+"))@(([[0-9]{1, 3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}])|(([a-zA-Z-0-9]+.)+[a-zA-Z]{2,}))$' ; String password_pattern = r'^[a-zA-Z0-9]{6,}$'; String mobile_pattern = r'^(?([0-9]{3}))?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$'; Size size; 152
@override Widget build(BuildContext context) { size=MediaQuery.of(context).size; return new Scaffold( key: _scafoldKey, body: Stack( children:<Widget>[ Image.asset("splash_img.png",fit: BoxFit.cover, width: size.width,height: size.height,), Container(color: const Color(0x99FFFFFF),), Container( height: 120, decoration: new BoxDecoration( border: Border.all(color: Colors.teal), borderRadius: BorderRadius.only(bottomLeft: Radius.circular(size.width/2),topRight: Radius.circular(size.width/2)), color: Colors.teal, ), ), Center( child: SingleChildScrollView( child: Padding( padding: EdgeInsets.only(left: 20,right: 20), child: Form( key: _formKey, child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ SizedBox(height: 20,), Container( decoration: new BoxDecoration( border: new Border.all(color: Colors.teal), borderRadius: BorderRadius.circular(10), color: Colors.teal, ), child: Padding( padding: const EdgeInsets.all(8.0), child: Text("Registration Form",style: TextStyle(color: Colors.white, fontSize: 22 ),), ), ), SizedBox(height: 40,), //--------------Name FormFiled------------------------------------------ TextFormField( controller: _nameEditController, textInputAction: TextInputAction.next, validator: (value){ if(value.isEmpty) { return "Enter Name"; 153
} return null; }, style: getTextStyle(), decoration: customInputDecoration("Enter Name"), ), SizedBox(height: 20,), //--------------Email FormFiled------------------------------------------ TextFormField( controller: _emailEditController, textInputAction: TextInputAction.next, validator: (value){ RegExp regex =RegExp(email_pattern); if (!regex.hasMatch(value)) return 'Enter Valid Email'; else return null; }, keyboardType: TextInputType.emailAddress, style: getTextStyle(), decoration: customInputDecoration("Enter email id"), ), SizedBox(height: 20,), //--------------Mobile FormFiled------------------------------------------ TextFormField( controller: _mobileEditController, textInputAction: TextInputAction.next, validator: (value){ RegExp regex =RegExp(mobile_pattern); if (!regex.hasMatch(value)) return 'Enter valid mobile number'; else return null; return null; }, keyboardType: TextInputType.number, maxLength: 10, style: getTextStyle(), decoration: customInputDecoration("Enter mobile number"), ), SizedBox(height: 20,), //--------------Password FormFiled------------------------------------------ TextFormField( controller: _passwordEditController, textInputAction: TextInputAction.done, validator: (value){ RegExp regex =RegExp(password_pattern); if (!regex.hasMatch(value)) return 'Password should be in alphanumaric with 6 characters'; else return null; }, obscureText: true, style: getTextStyle(), decoration: customInputDecoration("Enter password"), 154
), SizedBox(height: 20,), RaisedButton(onPressed: (){ if(_formKey.currentState.validate()) { UserDatabase.instance.insertUser(User(_nameEditController.text,_emailEditContr oller.text,_passwordEditController.text,_mobileEditController.text)).then((res ult){ if(result==-1) { _scafoldKey.currentState .showSnackBar(SnackBar(content: Text('User with same number already existed $result'))); }else { _scafoldKey.currentState .showSnackBar(SnackBar(content: Text('User Registered Succesfully $result'))); Navigator.pushReplacementNamed(context, "/login"); } }) ; } }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(18), ), color: Colors.pink, child: Text("Signup", style: TextStyle(color: Colors.white,fontSize: 20),), ), FlatButton( child: Text("Already have account, Sign In?"), onPressed: (){ Navigator.pushReplacementNamed(context, "/login"); }, ) ], ) ), ), ), ) ], ), );; } TextStyle getTextStyle(){ return TextStyle( fontSize: 18, color: Colors.pink 155
); } InputDecoration customInputDecoration(String hint) { return InputDecoration( hintText: hint, hintStyle: TextStyle( color: Colors.teal ), contentPadding: EdgeInsets.all(10), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: Colors.pink ) ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: Colors.pink ) ), ); } } Login Page import 'package:flutter/material.dart'; import 'package:flutter_firebase_app/databases/UserDatabase.dart'; class LoginPage extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return LoginState(); } } class LoginState extends State<LoginPage> { final _formKey = GlobalKey<FormState>(); final _scaffoldKey = GlobalKey<ScaffoldState>(); final _mobileController=TextEditingController(); final _passwordController=TextEditingController(); final FocusNode _mobileFocus = FocusNode(); final FocusNode _passwordFocus = FocusNode(); Size size; @override Widget build(BuildContext context) { size = MediaQuery.of(context).size; 156
return new Scaffold( key: _scaffoldKey, body: Stack( children:<Widget>[ Image.asset("splash_img.png",fit: BoxFit.cover, width: size.width, height: size.height, ), Padding( padding: EdgeInsets.only(left: 20,right: 20), child: Form( key: _formKey, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ SizedBox(height: 20,), TextFormField( controller: _mobileController, keyboardType: TextInputType.number, textInputAction: TextInputAction.next, focusNode: _mobileFocus, onFieldSubmitted: (term){ FocusScope.of(context).requestFocus(_passwordFocus); }, validator: (value){ if(value.isEmpty) { return "Enter mobile number"; } return null; }, style: getTextStyle(), decoration: customInputDecoration("Enter mobile number"), ), SizedBox(height: 20,), TextFormField( textInputAction: TextInputAction.done, controller: _passwordController, keyboardType: TextInputType.text, obscureText: true, focusNode: _passwordFocus, validator: (value){ if(value.isEmpty) { return "Enter Password"; } return null; }, style: getTextStyle(), decoration: customInputDecoration("Enter password"), ), SizedBox(height: 20,), RaisedButton(onPressed: (){ if(_formKey.currentState.validate()) { 157
UserDatabase.instance.checkUserLogin(_mobileController.text,_passwordControlle r.text).then((result){ if(result==null) { _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text("Please enter valid details"))); } else { Navigator.pushReplacementNamed(context, "/home"); } }); } }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(18), ), color: Colors.pink, child: Text("Login", style: TextStyle(color: Colors.white,fontSize: 20),), ), FlatButton( child: Text("Don't have account, Signup?"), onPressed: (){ Navigator.pushReplacementNamed(context, "/signup"); }, ) ], ) ), ) ] , ), ); } TextStyle getTextStyle(){ return TextStyle( fontSize: 18, color: Colors.pink ); } InputDecoration customInputDecoration(String hint) { return InputDecoration( hintText: hint, hintStyle: TextStyle( color: Colors.teal ), contentPadding: EdgeInsets.all(10), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: Colors.pink ) 158
), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: Colors.pink ) ), ); } } Home Page import 'package:flutter/material.dart'; import 'package:flutter_firebase_app/databases/UserDatabase.dart'; import 'package:flutter_firebase_app/models/user.dart'; class Homepage extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return HomeState(); } } class HomeState extends State<Homepage>{ Size size; User user; @override void initState() { // TODO: implement initState super.initState(); UserDatabase.instance.getUserData().then((result){ setState(() { user=result; }); }); } @override Widget build(BuildContext context) { size=MediaQuery.of(context).size; return Scaffold( appBar: AppBar( title: Text("Home"), ), body: Column( mainAxisSize: MainAxisSize.max, children: <Widget>[ Row( mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[ 159
Padding( padding: const EdgeInsets.all(12.0), child: RaisedButton( onPressed: (){ UserDatabase.instance.deleteUser(user.mobile).then((res){ if(res==1) { Navigator.pushReplacementNamed(context, "/login"); } }); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15), ), color:Colors.pink, child: Text("Logout", style: TextStyle(color: Colors.white ),) ), ) ], ), Container( height:size.height-200 , child: Center( child: (user==null)?null:Text("Welcome User "+user.name), ), ), ], ) ); } } Check sample at http://rrtutors.com/description/8 160
State Management We are always looking for a powerful way of state management. We knows flutter itself has provided us with state management Stateful widget Flutter has different ways of estate management topics Stateful Widget InheritedWidget Provider BLoC Stateful Widget Widgets which will change its behaviour/state dynamically called stateful widgets Stateful widgets are useful when the part of the user interface you are describing can change dynamically. User interfaces need to respond to a variety of things: The user doing something in the user interface. Receiving data from another computer. This is what Stateful Widgets are for. They store data (state) in an associated State class and they can respond when that data (state) changes as the result of the user doing something Let's create a Counter app with Stateful widget class MyStatefulwidget extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return MyState(); 161
} } class MyState extends State<MyStatefulwidget>{ int count; @override void initState() { // TODO: implement initState super.initState(); count=0; } @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text("Statefulwidget"),backgroundColor: Colors.pink,), floatingActionButton: FloatingActionButton(onPressed: (){ setState(() { count++; }); },child: Icon(Icons.add,color: Colors.white,),backgroundColor: Colors.pink,), body: Container( child: Center( child: Text("Count : $count",style: TextStyle(color: Colors.pink,fontSize: 20),), ), ), ); } } here on tap on add button every time we are updating the state by calling the setState() method, so this will entirely rebuild the widget This will be useful when a single widget needs to update the State. InheritedWidget Flutter provides an InheritedWidget that can define provide context to every widget below it in the tree. InheritedWidget is a special widget that can store and retrieve data , and subcomponents can obtain stored data. Commonly used MediaQuery and Theme are inherited from InheritedWidget 162
While this is nice in theory, we can see that it takes quite a lot of code to get a basic example wired up. Fortunately, there are libraries like Bloc, Redux, and Scoped Model abstract this complexity away Let's check the counter app with InheritedWidget Create InheritedWidget class CounterInherited extends InheritedWidget{ final Map _counter={'count':0}; Widget child; CounterInherited ({@required Widget this. child}):super(child:child); get counter=>_counter['count']; increment() { _counter['count']++; } @override bool updateShouldNotify(InheritedWidget oldWidget) { // TODO: implement updateShouldNotify return true; } static CounterInherited of(BuildContext ctx)=> ctx.inheritFromWidgetOfExactType(CounterInherited); } Here we are extending the class with InheritedWidget, which have override method will tell the widget to need to update state or not @override bool updateShouldNotify(InheritedWidget oldWidget) { // TODO: implement updateShouldNotify return true; } Create Child widget Child widget will have the counter increment UI 163
class Counter extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return CounterState(); } } class CounterState extends State<Counter>{ @override Widget build(BuildContext context) { int count=CounterInherited.of(context).counter; return Scaffold( appBar: AppBar(title: Text("InheritedWidget"),centerTitle: true,backgroundColor: Colors.pink,), floatingActionButton: FloatingActionButton(onPressed: (){ setState((){}); CounterInherited.of(context).increment(); },child: Icon(Icons.add,color: Colors.white,),backgroundColor: Colors.pink,), body: Container( child: Center( child: Text("Count : $count",style: TextStyle(color: Colors.pink,fontSize: 20),), ), ), ); } } This Child widget need parent widget instance to update counter value, This will done by static method of CounterInherited widget CounterInherited.of(context) Main Widget Now create a widget which contains parent widget as Inherited widget and pass child widget of our counter widget class MyInherited extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return CounterInherited( child: Counter(), ); } 164
} void main() => runApp(MyInherited ()); BLoC Flutter, however, brings a new reactive style that is not entirely compatible with MVC. Its design idea is to separate data from views, and render views by data mapping A variation of this classical pattern has emerged from the Flutter community – BLoC What is BLoC? BLoC stands for Business Logic Components, BLoC is a method of building applications using reactive programming. This is a completely asynchronous world composed of streams ● Wrap stateful components with StreamBuilder, streambuilder will listen for a stream ● This stream comes from BLoC ● The data in the stateful widget comes from the listening stream. ● User interaction gestures are detected and events are generated. For example, press the button. ● Call the function of bloc to handle this event ● After processing in bloc, the latest data will be added to the sink of the stream. ● StreamBuilder listens to new data, generates a new snapshot, and calls the build method again ● Widget is rebuilt 165
Example Here we coding a simple counter application with BLoC This Example show the Number counts in the first page, in the second page we are increase the counter number, this will reflect in the fistpage For this we are going to create app with below steps Create bloc model CountBLoC class CountBLoC{ int _count = 0; var _countController = StreamController<int>.broadcast(); Stream<int> get stream => _countController.stream; int get value => _count; addCount() { _countController.sink.add(++_count); } dispose() { _countController.close(); } } Create Provider class BlocProvider extends InheritedWidget{ CountBLoC bLoC = CountBLoC(); BlocProvider({Key key, Widget child}) : super(key: key, child: child); @override bool updateShouldNotify(InheritedWidget oldWidget) { // TODO: implement updateShouldNotify return true; } static CountBLoC of(BuildContext context) => (context.inheritFromWidgetOfExactType(BlocProvider) as BlocProvider).bLoC; } 166
Create First Page This Page will show the number counts, for this we are accessing the data with streambuilder class CountPage extends StatelessWidget{ @override Widget build(BuildContext context) { final bloc = BlocProvider.of(context); return Scaffold( backgroundColor: Colors.white, appBar: AppBar( title: Text('Counter Page',), backgroundColor: Colors.pink, ), body: Center( child: StreamBuilder<int>( stream: bloc.stream, initialData: bloc.value, builder: (BuildContext context, AsyncSnapshot<int> snapshot) { return Text( 'Number Counts : ${snapshot.data}',style: TextStyle(color: Colors.pink,fontSize: 20), ); }), ), floatingActionButton: FloatingActionButton( backgroundColor: Colors.pink, child: Icon(Icons.add,color: Colors.white,), onPressed: () => Navigator.of(context).push(MaterialPageRoute(builder: (context) => DisplayPage()))), ); } } Create Second page This Page will increase the count by tap on button. With calling the addCount function will increase the count value class DisplayPage extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build final bloc = BlocProvider.of(context); print('build'); return Scaffold( backgroundColor: Colors.white, appBar: AppBar( 167
title: Text('Update Count'), backgroundColor: Colors.pink, ), body: Center( child: StreamBuilder( stream: bloc.stream, initialData: bloc.value, builder: (context, snapshot) => Center( child: Text( " Counter : ${snapshot.data} nIncreate Count by Button tap", style: TextStyle(color: Colors.pink,fontSize: 20),textAlign: TextAlign.center,), )), ), floatingActionButton: FloatingActionButton( onPressed: () => bloc.addCount(), backgroundColor: Colors.pink, child: Icon(Icons.add,color: Colors.white,), ), ); } } Now It’s time to check out Main Page Here we are using the Provider to load the child widgets to Inherited widget. class MyBloc extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( child: MaterialApp( title: 'BLoC', theme: ThemeData.dark(), home: CountPage(), ), ); } } 168
Firebase Integration Firebase is a mobile and web application development platform developed by Firebase. Firebase supports frameworks like Flutter on a best-effort basis. Now we are going to learn how to setup firebase for Flutter Application Firebase Setup Step 1: Create a new Project in on firebase console (https://console.firebase.google.com/) This will ask to login with gmail. Login with your account. Now create new project in Firebase console After creating project it will navigates to Console dashboard 169
Now select Android, now it will display below screen 170
Add your application package name and register application Download google-service.json file and this under android->app folder 171
Modify your build.gradle files to use the plugin project level build.gradle file buildscript { repositories { // Check that you have the following line (if not, add it): google() // Google's Maven repository } dependencies { // ... // Add the following line: classpath 'com.google.gms:google-services:4.3.3' // Google Services plugin } } allprojects { // ... repositories { // Check that you have the following line (if not, add it): google() // Google's Maven repository // ... } } app level build.gradle file apply plugin: 'com.google.gms.google-services' That's it for Android setup iOS Setup Copy & Paste your Downloaded GoogleService-Info.plist into projectname/ios/Runner folder Open projectname/ios/PODFile (Execute pod install if file not found) & Add ; pod 'Firebase/Core; and execute pod install 172
Open projectname/ios/Runner.xcworkspace & Choose Project & Add File to Runner & Choose GoogleService-Info.plist with choose target of Runner 173
That's it now our application is ready for use Firebase Firebase authentication & Google sign in using Flutter Firebase Authentication provides backend services, easy-to-use SDKs, and ready-made UI libraries to authenticate users to your app. It supports authentication using passwords, phone numbers, popular federated identity providers like Google, Facebook and Twitter, and more Now we are going to learn how to set up and implementing Google Signin using Firebase authentication Step 1: Create Flutter application Step 2: Add dependencies Add required dependencies in pubspec.yaml file 174
dependencies: firebase_auth: ^0.15.4 google_sign_in: ^4.1.4 and run flutter packages get in terminal Step 3: import required packages in to the main file import 'package:firebase_auth/firebase_auth.dart'; import 'package:google_sign_in/google_sign_in.dart'; Step 4: Design Login Screen import 'package:flutter/material.dart'; class LoginPage extends StatefulWidget { @override _LoginPageState createState() => _LoginPageState(); } class _LoginPageState extends State<LoginPage> { @override Widget build(BuildContext context) { return Scaffold( body: Container( color: Colors.white, child: Center( child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ FlutterLogo(size: 150), SizedBox(height: 50), _signInButton(), ], ), ), ), ); } Widget _signInButton() { return OutlineButton( 175
splashColor: Colors.grey, onPressed: () {}, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(40)), highlightElevation: 0, borderSide: BorderSide(color: Colors.grey), child: Padding( padding: const EdgeInsets.fromLTRB(0, 10, 0, 10), child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Image(image: AssetImage("assets/google_logo.jpg"), height: 35.0), Padding( padding: const EdgeInsets.only(left: 10), child: Text( 'Sign in with Google', style: TextStyle( fontSize: 20, color: Colors.pink, ), ), ) ], ), ), ); } } Step 5: Authentication Now, we will need to create an instance of FirebaseAuth & GoogleSignIn final FirebaseAuth _auth = FirebaseAuth.instance; final GoogleSignIn googleSignIn = GoogleSignIn(); Future<String> signInWithGoogle() async { final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn(); final GoogleSignInAuthentication googleSignInAuthentication = await googleSignInAccount.authentication; final AuthCredential credential = GoogleAuthProvider.getCredential( accessToken: googleSignInAuthentication.accessToken, idToken: googleSignInAuthentication.idToken, ); final AuthResult authResult = await _auth.signInWithCredential(credential); final FirebaseUser user = authResult.user; assert(!user.isAnonymous); assert(await user.getIdToken() != null); final FirebaseUser currentUser = await _auth.currentUser(); assert(user.uid == currentUser.uid); 176
return 'signInWithGoogle succeeded: $user'; } void signOutGoogle() async{ await googleSignIn.signOut(); print("User Sign Out"); } Now update login button onPressed functionality onPressed: () { signInWithGoogle().whenComplete(() { Navigator.of(context).push( MaterialPageRoute( builder: (context) { return MyHomePage(); }, ), ); }); } Step 6: Check User Sign In We can check already user Signin or not by below code void isSignedIn() async { isLoggedIn = await googleSignIn.isSignedIn(); if (isLoggedIn) { Navigator.push( context, MaterialPageRoute(builder: (context) => MyHomePage()), ); } } 177
Complete code import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:google_sign_in/google_sign_in.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'homepage.dart'; class LoginPage extends StatefulWidget { @override _LoginPageState createState() => _LoginPageState(); } class _LoginPageState extends State<LoginPage> { final FirebaseAuth _auth = FirebaseAuth.instance; 178
final GoogleSignIn googleSignIn = GoogleSignIn(); bool isLoggedIn=false; @override void initState() { // TODO: implement initState super.initState(); isSignedIn(); } @override Widget build(BuildContext context) { return Scaffold( body: Container( color: Colors.white, child: Center( child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ FlutterLogo(size: 150), SizedBox(height: 50), _signInButton(), ], ), ), ), ); } Widget _signInButton() { return OutlineButton( splashColor: Colors.grey, onPressed: () { signInWithGoogle().whenComplete(() { Navigator.of(context).pushReplacement( MaterialPageRoute( builder: (context) { return MyHomePage(); }, ), ); }); }, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(40)), highlightElevation: 0, borderSide: BorderSide(color: Colors.grey), child: Padding( padding: const EdgeInsets.fromLTRB(0, 10, 0, 10), child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Image(image: AssetImage("assets/google_logo.jpg"), height: 35.0,width: 40,), Padding( padding: const EdgeInsets.only(left: 10), child: Text( 'Sign in with Google', style: TextStyle( 179
fontSize: 20, color: Colors.pink, ), ), ) ], ), ), ); } Future<String> signInWithGoogle() async { final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn(); final GoogleSignInAuthentication googleSignInAuthentication = await googleSignInAccount.authentication; final AuthCredential credential = GoogleAuthProvider.getCredential( accessToken: googleSignInAuthentication.accessToken, idToken: googleSignInAuthentication.idToken, ); final AuthResult authResult = await _auth.signInWithCredential(credential); final FirebaseUser user = authResult.user; assert(!user.isAnonymous); assert(await user.getIdToken() != null); final FirebaseUser currentUser = await _auth.currentUser(); assert(user.uid == currentUser.uid); return 'signInWithGoogle succeeded: $user'; } void signOutGoogle() async{ await googleSignIn.signOut(); print("User Sign Out"); } void isSignedIn() async { isLoggedIn = await googleSignIn.isSignedIn(); if (isLoggedIn) { Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => MyHomePage()), ); } } } 180
Chat Application with Firebase database Flutter Now we are going to learn how to create Chat app with Firebase Database Step 1: Create flutter application Step 2: Create Users list page Homepage.dart In this Page we are fetching all the users data from Firebase database and display import 'dart:async'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_database/firebase_database.dart'; import 'package:flutter/material.dart'; import 'package:flutter_firbase/user.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'chatpage.dart'; class MyHomePage extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return MyHomePageState(); } } class MyHomePageState extends State<MyHomePage> { FirebaseDatabase fireDB=FirebaseDatabase.instance; TextEditingController _txtCtrl = TextEditingController(); FirebaseUser currentUser; String id; String nickname=""; String photoUrl; List<User>listUsers=new List(); @override void initState() { // TODO: implement initState super.initState(); getUserInfo(); } getUserInfo() async { 181
SharedPreferences prefs = await SharedPreferences.getInstance(); setState(() { id=prefs.getString("id"); nickname=prefs.getString("nickname"); photoUrl=prefs.getString("photoUrl"); }); FirebaseDatabase.instance.reference().child('users/-M1ZPGcD23x7uAUew5fF') .orderByChild("created_at") .onValue .listen((event) { print("data user"); Map<dynamic,dynamic>hm=event.snapshot.value; if(hm!=null) { hm.forEach((key, value) { print(hm[key]); }); } }); print("Fetching datad 0 "); StreamSubscription<Event> subscription =fireDB .reference() .child("users") .onValue .listen((Event event) { print("Fetching datad 1 "); print( event.snapshot.value); Map<dynamic,dynamic>hm=event.snapshot.value; hm.forEach((key, value) { Map<dynamic,dynamic>users=value; setState(() { if(users['id']!=id) listUsers.add(new User(users['name'],users['id'],users['photoUrl'])); }); }); },onError: (errror){ print("Error ${errror.toString()}"); }); } @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text("Chat $nickname"),), body: Column( mainAxisAlignment: MainAxisAlignment.start, children: <Widget>[ Expanded( child:(listUsers.length>0)? ListView.separated( separatorBuilder: (context, index) { return Divider( color: Colors.black, 182
); }, itemCount: listUsers.length, itemBuilder: (context,index){ return ListTile( title: Text(listUsers[index].name), leading: Material(child: Image.network(listUsers[index].img,),borderRadius: BorderRadius.all( Radius.circular(32.0), ), clipBehavior: Clip.hardEdge,), onTap: (){ Navigator.push(context, MaterialPageRoute(builder: (context){ return ChatPage(myid: id,uid:listUsers[index].id ,myImg: photoUrl,uImg: listUsers[index].img,myName: nickname,uName:listUsers[index].name ,); })); }, ); }):Center(child: CircularProgressIndicator(),) ), Container( margin: EdgeInsets.all(8), ) ]), ); } String peerId="1"; String peerAvatar; var listMessage; String groupChatId="1"; SharedPreferences prefs; } Step 3: Create Chat page chatpage.dart In this page we are sending the Messages to selected user and display list of messages between two users import 'dart:async'; import 'dart:convert'; import 'package:firebase_database/firebase_database.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'message.dart'; class ChatPage extends StatefulWidget{ 183
const ChatPage({@required this.myid,@required this.uid,@required this.myName,@required this.uName,@required this.myImg,@required this.uImg}); final String myid; final String uid; final String myName; final String uName; final String myImg; final String uImg; @override State<StatefulWidget> createState() { // TODO: implement createState return ChatPageState(myid,uid,myName,uName,myImg,uImg); } } class ChatPageState extends State{ final String myid; final String uid; final String myName; final String uName; final String myImg; final String uImg; TextEditingController _txtCtrl=new TextEditingController(); FirebaseDatabase fireDB=FirebaseDatabase.instance; ChatPageState( this.myid, this.uid,this.myName, this.uName,this.myImg,this.uImg); List<Messages>list; @override void initState() { // TODO: implement initState super.initState(); list=new List(); fetchMessages(); /*FirebaseDatabase().reference() .child("messages") .child(myid+"_"+uid) .limitToLast(1) .orderByChild("created_at") .onChildAdded .listen((event) { Map<dynamic,dynamic>hm=event.snapshot.value; print("Event ${hm.length}"); int k=0; hm.forEach((key, value) { print("Fetching Msgs "); print(key); if(k==hm.length) { setState(() { Map map=hm[key]; list.add(Messages.fromMap(value,key)); }); } k++; }); 184
});*/ } @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar( leading: Material(child: Image.network(uImg,width: 10,height: 10,),borderRadius: BorderRadius.all( Radius.circular(32.0), ), clipBehavior: Clip.hardEdge,), title: Text(uName), backgroundColor: Colors.pink, ), body: Container( child: Column( children: <Widget>[ Expanded( child: (list.length>0)?ListView.separated( separatorBuilder: ( context, index) => Divider(color: Colors.grey,), itemCount: list.length, reverse: false, itemBuilder: (context, index) { return buildItem(index,list[index]); } ,):Center(child: CircularProgressIndicator(), ), ), Container( margin: EdgeInsets.all(8), child: Row(children: <Widget>[ Expanded(child: TextField( controller: _txtCtrl,decoration: InputDecoration( hintText: "Write your Message", border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: Colors.pink) )),)), SizedBox( width: 80, child: FloatingActionButton( onPressed: () => sendMessage(), child: Icon(Icons.send), )) ])) ], ), ), ); } sendMessage() { fireDB .reference() .child("messages") .child(getUniqueId(myid,uid)) .child(DateTime.now().toUtc().millisecondsSinceEpoch.toString()) .push().set({ 185
"from_id": myid, "to_id": uid, "from_name": myName, "to_name": uName, "msg": _txtCtrl.text }).then((value) => { _txtCtrl.text = '' }); } fetchMessages() { fireDB .reference() .child("messages") .child(getUniqueId(myid,uid)) .onValue .listen((Event event) { print("Fetching datad"); print( event.snapshot.value); Map<dynamic,dynamic>hm=event.snapshot.value; if(hm!=null) { list.clear(); hm.forEach((key, value) { print("Fetching Msgs $value"); print(key); Map m=value; m.forEach((key, value) { setState(() { list.add(Messages.fromMap(value,key)); }); }); }); } }); //StreamSubscription<Event> subscription = } Widget buildItem(int index, Messages msg) { if (msg.from_id == myid) { // Right (my message) return Column( children: [ Container( child: Column( mainAxisSize: MainAxisSize.min, 186
crossAxisAlignment: CrossAxisAlignment.end, children: <Widget>[ Text( msg.msg, style: TextStyle(color: Colors.black), ), /*SizedBox(height: 10,), (msg.created_at!=null)? Text( "${readTimestamp(msg.created_at )}", style: TextStyle(color: Colors.black,fontSize: 8), ):Text("")*/ ], ), padding: EdgeInsets.fromLTRB(15.0, 10.0, 15.0, 10.0), width: 200.0, decoration: ShapeDecoration(shape: OutlineInputBorder(borderRadius: BorderRadius.circular(8),borderSide: BorderSide(color: Colors.pink))), margin: EdgeInsets.only(bottom: 10.0, right: 10.0), ) ], mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end, ); } else { // Left (peer message) return Container( child: Column( children: <Widget>[ Container( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: <Widget>[ Text( msg.msg, style: TextStyle(color: Colors.black), ), /*SizedBox(height: 10,), (msg.created_at!=null)? Text( ("${readTimestamp(msg.created_at )}"), style: TextStyle(color: Colors.black,fontSize: 8), ):Text("")*/ ], ), padding: EdgeInsets.fromLTRB(15.0, 10.0, 15.0, 10.0), width: 200.0, decoration: ShapeDecoration(shape: OutlineInputBorder(borderRadius: BorderRadius.circular(8))), margin: EdgeInsets.only(left: 10.0), ) ], crossAxisAlignment: CrossAxisAlignment.start, ), margin: EdgeInsets.only(bottom: 10.0), ); } } 187
static String getUniqueId(String i1,String i2){ if(i1.compareTo(i2)<=-1){ return i1+i2; } else{ return i2+i1; } } String readTimestamp(DateTime timestamp) { var now = DateTime.now(); return now.difference(timestamp).inHours.toString(); /* var format = DateFormat('HH:mm a'); var date = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000); var diff = now.difference(date); var time = ''; if (diff.inSeconds <= 0 || diff.inSeconds > 0 && diff.inMinutes == 0 || diff.inMinutes > 0 && diff.inHours == 0 || diff.inHours > 0 && diff.inDays == 0) { time = format.format(date); } else if (diff.inDays > 0 && diff.inDays < 7) { if (diff.inDays == 1) { time = diff.inDays.toString() + ' DAY AGO'; } else { time = diff.inDays.toString() + ' DAYS AGO'; } } else { if (diff.inDays == 7) { time = (diff.inDays / 7).floor().toString() + ' WEEK AGO'; } else { time = (diff.inDays / 7).floor().toString() + ' WEEKS AGO'; } } return time;*/ } } Step 5: Run application 188
189

Flutter tutorial for Beginner Step by Step

  • 2.
    Table of Content Introduction5 Flutter Installation 6 Application Folder Structure 10 Dart Basics 11 Variables 11 Defining a map 14 Functions 15 Named parameter 16 Default value parameter 16 Control flow 17 Loops 17 For Loop 17 While loop 18 Do-while loop 18 Switch 18 Exception handling 19 Flutter - Widgets 21 What is a widget? 21 Platform specific widgets 22 Material Widgets 22 Cupertino Widgets 23 Layout Widgets 23 Single Child Widgets 24 Multi Child Widgets 24 Example of widgets and Layout widgets 25 Image 26 Load Network Image 26 Load Image from Assets 27 Icon 29 Buttons 30 Button Examples 31 Multi Child Layouts 35 Flutter - Linear Layout 35 Framelayout in Flutter 38 2
  • 3.
    Flex Widget 39 LikeRow Widget 41 Like Column Widget 42 Weight Property like Android in Flex widget 43 Listview 44 How will we handle the item click events? 45 Dynamic ListView 46 Listview.separated 48 Examples of Single Child Layout Widgets 50 Container 50 Card 52 Expanded 54 Flexible 57 Center 58 GestureDetector 58 Positioned 61 SafeArea 64 SingleChildScrollView 64 Themes 67 Scaffold 68 Dialogs 73 Simple AlertDialog 73 Add buttons to Alert Dialogs 74 How to close Dialog 74 ExpansionPanelList & ExpansionPanel 78 ExpansionPanel 78 ExpansionPanelList 78 GridView 83 PopupMenu 86 Checked Widgets 88 CheckBox 88 CheckboxListTile 89 Radio 89 TabBar TabBarView 95 Table 97 Future and FutureBuilder 101 FutureBuilder 102 3
  • 4.
    StreamBuilder 105 Navigation 108 Routingtable (Named Routes) 113 Navigation switch animation 117 Form & Form Fields 118 Form State 118 Form Validation 119 Input Decoration Themes 123 Networking & JSON and Serialization 126 HTTP Introduction 126 HTTPClient 126 Make HTTPRequest 126 Decode and encode JSON 127 HTTP Library 131 CompleteExample 135 Database and local storage 141 SharedPreferences 141 How to access Shared Preferences in Flutter? 141 Database 145 How to access SQLite in Flutter? 145 SQFlite Features 145 Database operations 147 Create Table 147 Query 149 State Management 161 Stateful Widget 161 InheritedWidget 162 BLoC 165 What is BLoC? 165 Example 166 Firebase Integration 169 Firebase Setup 169 Firebase authentication & Google sign in using Flutter 174 Chat Application with Firebase database Flutter 181 4
  • 5.
    Introduction Flutter is anopen-source UI software development kit created by Google. It is used to develop applications for Android, iOS, Windows, Mac, Linux, Google Fuchsia and the web. Flutter is Google's mobile UI framework that can quickly build high-quality native user interfaces on iOS and Android. Flutter works with existing code. Flutter is being used by more and more developers and organizations around the world, and Flutter is completely free and open source . At present, some modules of the company are developed using Flutter . The major components of Flutter include: Dart platform Flutter engine Foundation library Design-specific widgets Dart Platform Flutter apps are written in the Dart language and make use of many of the language's more advanced features You can refer Dart Language at Dart 5
  • 6.
    Flutter Installation Flutter issupporting HybridApp development on different Os. To set up the flutter on each individual os by this Flutter official Tutorial In this section we will learn how to install Flutter SDK in Windows system. Step 1: Download Flutter SDK from Official website The Latest version is 1.12 Step 2: Unzip and archive file in specific folder, like c:flutter Step 3: Update the system path to include flutter bin directory Step 4: Open terminal and move to installed directory and run flutter doctor flutter doctor is tool to check all requirements for the flutter is installed or not and show the details The result of the above command will give you Doctor summary (to see all details, run flutter doctor -v): [√] Flutter (Channel master, v1.14.6-pre.51, on Microsoft Windows [Version 6.3.9600], locale en-IN) [√] Android toolchain - develop for Android devices (Android SDK version 28.0.3) [√] Chrome - develop for the web [√] Android Studio (version 3.3) [!] Android Studio (version 3.4) 6
  • 7.
    X Flutter pluginnot installed; this adds Flutter specific functionality. X Dart plugin not installed; this adds Dart specific functionality. [√] Connected device (2 available) ! Doctor found issues in 1 category. Step 5: Install Android Studio Step 6: Check Latest Android SDK installed or not, if not install it Step 7: Open Android studio and install Dart and Flutter Plugins for Android studio. ● Click File > Settings > Plugins. ● Select the Flutter plugin and click Install. ● Click Yes when prompted to install the Dart plugin. ● Restart Android studio Flutter – Creating Simple Application in Android Studio Now Open File -> Create new Flutter Project It will prompt below screen 7
  • 8.
    Select Flutter applicationand press Next Now it will ask below details 8
  • 9.
    ● Enter yourProject Name ● Set Flutter SDk path (It is installation path) ● Set the Project Location ● Press Next Button Enter Domain name and Press Finish Now it will create a project with auto generated code Now connect real device/Emulator and run the application The output will be like this 9
  • 10.
    Application Folder Structure Tounderstand flutter fully we need to understand the first Flutter folder structure. ● android - Auto generated source code to create android application ● ios - Auto generated source code to create ios application ● web- Auto generated source code to create web application ● lib - Main folder containing Dart code written using flutter framework ● lib/main.dart - Entry point of the Flutter application ● test - Folder containing Dart code to test the flutter application ● test/widget_test.dart - Sample code ● .gitignore - Git version control file ● .metadata - auto generated by the flutter tools ● .packages - auto generated to track the flutter packages ● .iml - project file used by Android studio ● pubspec.yaml - Used by Pub, Flutter package manager ● pubspec.lock - Auto generated by the Flutter package manager, Pub ● README.md - Project description file written in Markdown format 10
  • 11.
    Dart Basics Dart isan open-source general-purpose programming language. It was originally developed by Google. Dart is an object-oriented language with C-style syntax. It supports programming concepts like interfaces, classes, unlike other programming languages Dart doesn’t support arrays. Dart collections can be used to replicate data structures such as arrays, generics, and optional typing. The following code shows a simple Dart program void main() { print('Hello, World!'); } Every Dart application contains main() functions, from here code will execute. Variables Variable is named storage location and Data types simply refers to the type and size of data associated with variables and functions. Dart uses a var keyword to declare the variable. The syntax of var is defined below var name = 'Flutter'; Dart provide us various built in data types Numbers 11
  • 12.
    As Other Programminglanguages to Java or C++ Dart does not have anything like float or long. Dart offers just two types of number Int Double Strings : It represents a sequence of characters. String values are specified in either single or double quotes Booleans : Dart uses the bool keyword to represent Boolean values – true and false Lists & Maps : It is used to represent a collection of objects Runes : Represents string Unicode coded characters (UTF-32 code points), etc Symbols : Use a Symbol literal to obtain the symbol's symbol object, which is to add a # symbol in front of the identifier String name = 'Flutter'; Here String is Data type, name is variable name and Flutter is value of variable var name = 'Flutter'; The type of the name variable is inferred as String. The compiler will check we have var keyword not String so type depends on value which in this case is String Example: void main() { String fName = 'Chandu'; var lName='Mouli'; int intValue = 123; print(fName); print(lName); 12
  • 13.
    print(intValue); } Output will be Chandu Mouli 123 List: Declare a list is very simple, you can simply use square brackets [] to define the list. The following are common operations for lists main(List<String> args) { var list = [1,2,3,4]; print(list); //Output: [1, 2, 3, 4] //Length print(list.length); //Selecting single value print(list[1]); //Output: 2 //Adding a value list.add(10); //Removing a single instance of value list.remove(3); //Remove at a particular position list.removeAt(0); } If we want to define a compile-time constant list, for example, the contents of the list are immutable, you can use keywords const 13
  • 14.
    main(List<String> args) { varlist = const [1,2,3,4]; } Defining a map We can define maps by using curly braces {} main(List<String> args) { var map = { 'key1': 'value1', 'key2': 'value2', 'key3': 'value3' }; //Fetching the values print(map['key1']); //Output: value1 print(map['test']); //Output: null //Add a new value map['key4'] = 'value4'; //Length print(map.length); //Check if a key is present map.containsKey('value1'); //Get entries and values var entries = map.entries; var values = map.values; } We can also define Map by constructor 14
  • 15.
    main(List<String> args) { varsquares = new Map(); squares[4] = 16; } Functions Functions in dart are similar to those in JavaScript. It consists of the function name, return value, and parameters. main(List<String> args) { var name = fullName('Chandu', 'Mouli'); print(name); } String fullName(String firstName, String lastName) { return "$firstName $lastName"; } return type is an option, if we can remove return type and function looks like below main(List<String> args) { var name = fullName('Chandu', 'Mouli'); print(name); } fullName(String firstName, String lastName) { return "$firstName $lastName"; } 15
  • 16.
    If the functionhaving single line we can write it as main(List<String> args) { var name = fullName('Chandu', 'Mouli'); print(name); } fullName(String firstName, String lastName) => "$firstName $lastName"; Named parameter Dart provides Named parameters while calling the functions main(List<String> args) { var name = fullName(firstName: 'Chandu', lastName: 'Mouli'); print(name); } fullName({String firstName, String lastName}) { return "$firstName $lastName"; } Default value parameter If we didn't pass parameter to function call we can define a default value to the parameters main(List<String> args) { 16
  • 17.
    var name =fullName(firstName: 'Chandu'); print(name); } fullName({String firstName, String lastName = "Shekar"}) { return "$firstName $lastName"; } Control flow If - else This is similar to other programing languages If - else main(List<String> args) { var day = 6; if (number > 7) { print('Not a Week Day'); } else if (number < 100) { print('Week Day'); } } Loops For Loop main(List<String> args) { for (int i = 0; i < 10; i++) { print('$i'); } } 17
  • 18.
    While loop main(List<String> args){ int i = 0; while(i < 10) { print('$i'); i++; } } Do-while loop main(List<String> args) { int i = 0; do { print('$i'); i++; } while (i < 10); } Switch main(List<String> args) { int day = 5; switch(age) { case 1: print('Sunday.'); break; case 2: print('Monday.'); break; 18
  • 19.
    case 3: print('Tuesday'); break; case 4: print('Wednesday'); break; case5: print('Thursday'); break; case 6: print('Friday'); break; case 7: print('Saturday'); break; } } Exception handling Similar to other programing languages dart also we can handle exceptions by Try, catch blocks and throw exceptions by throw keyword main(List<String> args) { divide(10, 0); } divide(int a, int b) { if (b == 0) { throw new IntegerDivisionByZeroException(); } return a / b; } 19
  • 20.
    Let’s catch theexception pass catch block main(List<String> args) { try { divide(10, 0); } on IntegerDivisionByZeroException { print('Division by zero.'); } } divide(int a, int b) { if (b == 0) { throw new IntegerDivisionByZeroException(); } return a / b; } 20
  • 21.
    Flutter - Widgets Nowyou are not having knowledge on flutter basics then go with Technical overview What is a widget? Everything within a flutter application is widget. From basic "text","Buttons" to "Screen Layouts". In flutter application everything is designed by widget only. These widgets are arranged in hierarchical order to be displayed onto the screen. In the Flutter widgets most of widgets are Container widgets unlike Text widget Widgets are two types ● Stateless Widget ● Stateful widget All Widgets are categorized into below groups 1. Platform specific widgets 2. Layout widgets 3. State maintenance widgets 4. Platform independent / basic widgets 21
  • 22.
    Platform specific widgets Flutterprovides platform specific widgets like Android and Ios Android specific widgets are designed based on Material design rules These widgets are called Material Widgets Ios specific widgets are designed based on Human Interface Guidelines by Apple, These widgets are called Cupertino widgets Material Widgets Scaffold AppBar BottomNavigationBar TabBar TabBarView ListTile RaisedButton FloatingActionButton FlatButton IconButton DropdownButton PopupMenuButton ButtonBar TextField Checkbox Radio Switch Slider Date & Time Pickers 22
  • 23.
  • 24.
  • 25.
    Example of widgetsand Layout widgets First create a simple visible widget Text("Text widget") If we want to add this visible widget to the Screen we need to put this widget inside the layout widget. Let's create layout widget Container( child: Text("Text Widget") ) Now let;s add this Lyoutwidget to the screen by class Sample1 extends StatelessWidget{ @override Widget build (BuildContext context) { return Container( child: Text("Text Widget"); ) } } 25
  • 26.
    Image Image widget usedto show an image. When displaying an image, you specify the image source in the constructor: image provider asset, network, file, memory Load Network Image class ImageWidget extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( home: SafeArea( child: Scaffold( body: Center(child: Image.network("https://cdn.pixabay.com/photo/2016/07/0 3/16/06/global-warming-1494965_960_720.jpg",width: 200,)) ), ), ); } } 26
  • 27.
    Load Image fromAssets To load images from assets first we need to create an Assets folder inside the application. It could be like below Now after adding image into assets folder we need to set the path inside pubspec.yaml file 27
  • 28.
    Next run thebelow command in terminal to configure image flutter packages get Now lets create sample class ImageWidget extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( home: SafeArea( child: Scaffold( body: Center(child: Image.asset("assets/user.png",width: 200,)) ), ), ); } } While Loading the images there is no option to show placeholder with above way, Then how to show Placeholder while loading the image. With FadeInImage widget we can achieve to show placeholder image Replace above code with Scaffold( body: Center(child: FadeInImage.assetNetwork(placeholder: "assets/user.png", image: "https://cdn.pixabay.com/photo/2016/07/03/16/06/globa l-warming-1494965_960_720.jpg",width: 200,)) ) 28
  • 29.
    Icon The icon widgetallows us to quickly build icon widgets using a pre-built list of material icons, available in the Icons class. We can specify the icon size and color class IconWidget extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( home: SafeArea( child: Scaffold( 29
  • 30.
    body: Center(child: Icon(Icons.email,color:Colors.pink,size: 48,)) )) , ); } } Buttons We can’t imagine a programming language without click events. Similarly other languages flutter provided buttons to handle click events. We have different types of buttons in flutter FlatButton RaisedButton IconButton OutlineButton DropdownButton BackButton CloseButton FloatingActionButton All these buttons click event is handled by onPressed() onPressed: (){ } FlatButton : This type of button doesn't have any border, When we click on it it will show press effect RaisedButton : When we need to show some decoration we can use this button 30
  • 31.
    IconButton : Itis a material button, Flashes background circle when clicked on OutlineButton : A bordered button whose elevation increases and whose background becomes opaque when the button is pressed DropDownButton : It is a Material widget, Which is used for selecting from a list of items It similar to Spinner in Android CloseButton : An IconButton setup for use as a close button to close modals (or any other closeable content). Flashes background circle when clicked on FloatingActionButton: It is a Material widget button, A button that hovers in a layer above content Button Examples class ButtonWidget extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return ButtonsWidgetState(); } } class ButtonsWidgetState extends State<ButtonWidget>{ var selected_item="Please choose a location"; List<String>list=[ "Please choose a location", "Item One", "Item Two", "Item Three", "Item Four", ]; 31
  • 32.
    @override Widget build(BuildContext context){ // TODO: implement build return MaterialApp( home: SafeArea(child: Scaffold( appBar: AppBar(title: Text("Buttons"),backgroundColor: Colors.pink,), body: Container( child: Center( child: Column( children: <Widget>[ FlatButton(onPressed: (){ debugPrint('Button Clicked '); }, child: Text("Flat Button")), RaisedButton(onPressed: (){ debugPrint('Button Clicked '); },child: Text("Raised Button"),), OutlineButton(onPressed: (){ debugPrint('Button Clicked '); },child: Text("Outline Button"),highlightedBorderColor: Colors.pink,), IconButton(onPressed: (){ debugPrint('Button Clicked '); },icon: Icon(Icons.add),color: Colors.pink,), DropdownButton( items:list.map((value){ return DropdownMenuItem(child: Text(value),value: value); }).toList(), hint: Text("Please choose a location"), value: selected_item, onChanged: (value){ 32
  • 33.
    selected_item=value; setState(() { }); debugPrint('Changed: ${value}'); }, ) BackButton(onPressed:(){ debugPrint('Button Clicked '); },color: Colors.pink,), CloseButton(), FloatingActionButton(onPressed: (){ debugPrint('Button Clicked '); }, child: Icon(Icons.search),backgroundColor: Colors.pink,) ], ), ), ), )), ); } } 33
  • 34.
  • 35.
    Multi Child Layouts Flutter- Linear Layout In Android we have a linear layout to arrange childs in horizontal and vertical, similarly in flutter we can arrange by Row, Column widgets. Example Horizontal Arrangement by Row class RowWidget extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Tutorial', theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( body: SafeArea(child: Container( color: Colors.brown, child: Row( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Text("Header",style: TextStyle(color: Colors.white),), Icon(Icons.account_circle,size: 100,color: Colors.white,), Text("Name ",style: TextStyle(color: Colors.white)) ], ), )), ), 35
  • 36.
    ); } } Vertical Arrangement byColumn class ColumnWidget extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Tutorial', theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( body: SafeArea(child: Container( color: Colors.brown, child: Column( mainAxisSize: MainAxisSize.max, children: <Widget>[ Text("Header",style: TextStyle(color: Colors.white),), Icon(Icons.account_circle,size: 100,color: Colors.white,), Text("Name ",style: TextStyle(color: Colors.white)) ], ), )), ), ); } } 36
  • 37.
    We can seethe arrangement of children horizontal/vertical in below screen Row wrap_content Row match_parent Column wrap_content Column match_parent How to set the Gravity for these widgets We can set the Gravity by using CrossAxisAlignment How it will work for Row and Column widgets If we set the property for the Row it will align based on Vertical direction(center,start,end…) If we set the property for Column it will align based on Horizontal direction(center,start,end…) 37
  • 38.
    Framelayout in Flutter Flutteruses Stack widgets to control child widgets at a layer. Child widgets can completely or partially cover the base widgets. Stack control positions its children relative to the edges of its box. This class is useful if you just want to overlap multiple child widgets. class StackWidget extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Tutorial', theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( body: SafeArea(child: Center( child: Stack( alignment: const Alignment(0, 0), children: <Widget>[ Image.network("https://cdn.pixabay.com/photo/2017/04/23 /19/17/climate-change-2254711_960_720.jpg"), Container( decoration: BoxDecoration( color: Colors.white, ), child: Text('GLobal Warming',style: TextStyle(fontSize: 20),), ), ], 38
  • 39.
    ), )), ), ); } } Flex Widget The FlexWidget is similar to Row and Column widget. We can use it as Row and Column by specifying the direction property. class FlexWidget extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Tutorial', theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( body: SafeArea(child: Container( color: Colors.brown, child: Flex( direction: Axis.vertical, mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Container(child: Padding( padding: const EdgeInsets.all(8.0), child: Text("Header",style: TextStyle(color: Colors.white),), 39
  • 40.
    ),color: Colors.green,), Container(child: Icon(Icons.account_circle,size:100,color: Colors.white,),color: Colors.yellow,), Container(child: Padding( padding: const EdgeInsets.all(8.0), child: Text("Name ",style: TextStyle(color: Colors.white)), ),color: Colors.pink,) ], ), ), ), ), ); } } Change different alignment and check the result 40
  • 41.
    Like Row Widget DirectionHorizontal wrap_content Direction Horizontal match_parent 41
  • 42.
    Like Column Widget DirectionVertical wrap_content Direction Vertical match_parent 42
  • 43.
    Weight Property likeAndroid in Flex widget If we add more items inside flex , to fit all these we can use Expandable widget to set each child flex . Flex( direction: Axis.horizontal, mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Flexible( flex: 1, child: Container(child: Padding( padding: const EdgeInsets.all(8.0), child: Text("Header",style: TextStyle(color: Colors.white),), ),color: Colors.green,), ), Flexible(flex: 1,child: Container(child: Icon(Icons.account_circle,size: 100,color: Colors.white,),color: Colors.yellow,)), Flexible(flex: 1, child: Container(child: Padding( padding: const EdgeInsets.all(8.0), child: Text("Name ",style: TextStyle(color: Colors.white)), ),color: Colors.pink,), ), ], ) 43
  • 44.
    Listview If we havemore items to make them scrollable if the screen of the user device is smaller than the content of the control. In Flutter, the easiest way is to use ListView Here simple listview example class ListViewWidget extends StatefulWidget { ListViewWidget({Key key}) : super(key: key); @override ListViewWidgetState createState() => ListViewWidgetState(); } class ListViewWidgetState extends State<ListViewWidget> { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( backgroundColor: Colors.pink, title: Text("Listview"), ), body: _getListData(), ), ); } _getListData() { List<Widget> widgets = []; for (int i = 0; i < 100; i++) { widgets.add( Card( margin: EdgeInsets.all(5), 44
  • 45.
    child: ListTile( title: Text("Row$i"), leading: Icon(Icons.account_circle), trailing: Icon(Icons.arrow_forward_ios,size: 14,), ), )); } return ListView(children: widgets); } } How will we handle the item click events? ListTile has the property of onTap() function, with this we can handle the Click events of each child item. onTap: (){ _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text("Clicked on Child $i"))); }, 45
  • 46.
    Dynamic ListView The aboveexample shows all static static widgets data. If we want to show dynamic data then we need to use ListView.Builder() class ListViewWidget extends StatefulWidget { ListViewWidget({Key key}) : super(key: key); 46
  • 47.
    @override ListViewWidgetState createState() =>ListViewWidgetState(); } class ListViewWidgetState extends State<ListViewWidget> { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( backgroundColor: Colors.pink, title: Text("Listview"), ), body: _getDynamicList(), ), ); } _getDynamicList() { var countries = ['Albania', 'Andorra', 'Armenia', 'Austria', 'Azerbaijan', 'Belarus', 'Belgium', 'Bosnia and Herzegovina', 'Bulgaria', 'Croatia', 'Cyprus', 'Czech Republic', 'Denmark', 'Estonia', 'Finland', 'France', 'Georgia', 'Germany', 'Greece', 'Hungary', 'Iceland', 'Ireland', 'Italy', 'Kazakhstan', 'Kosovo', 'Latvia', 'Liechtenstein', 'Lithuania', 'Luxembourg', 'Macedonia', 'Malta', 'Moldova', 'Monaco', 'Montenegro', 'Netherlands', 'Norway', 'Poland', 'Portugal', 'Romania', 'Russia', 'San Marino', 'Serbia', 'Slovakia', 'Slovenia', 'Spain', 'Sweden', 'Switzerland', 'Turkey', 'Ukraine', 'United Kingdom', 'Vatican City']; return ListView.builder( itemCount: countries.length, itemBuilder: (ctx,index){ return ListTile( onTap: (){ _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text("Clicked on Country ${countries[index]}"))); 47
  • 48.
    }, title: Text(countries[index]), leading: Icon(Icons.flag), trailing:Icon(Icons.arrow_forward_ios,size: 14,), ); }); } } Listview.separated class ListViewBuilderWidget extends StatefulWidget{ @override 48
  • 49.
    State<StatefulWidget> createState() { returnnew _ListViewBuilderWidget (); } } class _ListViewBuilderWidget extends State<ListViewBuilderWidget>{ @override Widget build(BuildContext context) { return Scaffold( appBar: new AppBar( title: new Text("ListviewBuilder Widget"), ), body: ListView.separated( itemCount: 100, itemBuilder: (BuildContext context, int index) { return ListTile(title: Text(" $index - ", style: TextStyle(color: Colors.blue),)); }, separatorBuilder: (BuildContext context, int index) { return Divider(color: Colors.blue, height: 10,); } ), ); } } 49
  • 50.
    Examples of SingleChild Layout Widgets Container A convenience widget that combines common painting, positioning, and sizing widgets. Often used to contain wrap child widgets and apply styling Container having the below properties Color Property Child Property Alignment Property Constraints Property Margin Property Padding Property Decoration Property ForegroundDecoration Property Transform Property class ContainerWidget extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( home: Scaffold( appBar: AppBar(title: Text("Container"),), body: Container( color: Color.fromARGB(255, 66, 165, 245), child: Container( color: Colors.pink, alignment: Alignment.center, constraints: BoxConstraints( maxHeight: 300, 50
  • 51.
    maxWidth: 200, minWidth: 150, minHeight:150, ), child: Container( child: Text("Flutter Cheatsheet", style: TextStyle( fontSize: 30.0, color: Colors.white, ),),), transform: Matrix4.rotationZ(0.5), ), alignment: Alignment.center, ),),); }} 51
  • 52.
    Card A card-like widget.Similar to Android's CardView, the card has slightly rounded corners and shadows Card Widget attribute color : container background color elevation : Z-axis height, to achieve the shadow effect. shape : defines the shape of the container margin : margin clipBehavior : the way to clip content Example: class CardWidget extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( home: Scaffold( appBar: AppBar(title:Text("Card Widget"),backgroundColor: Colors.pink,), body: Container( alignment: Alignment.topCenter, margin: EdgeInsets.only(top: 10.0), child: SizedBox( width: 400.0, height: 200.0, child: Card( color: Colors.purple, elevation: 30.0, child: Padding( padding: EdgeInsets.all( 14.0, 52
  • 53.
    ), child: Column( children: <Widget>[ Row( children:<Widget>[ CircleAvatar( backgroundImage: NetworkImage( "https://cdn.pixabay.com/photo/2016/10/17/17/41/priyan ka-chopra-1748248_960_720.jpg"), radius: 24.0, ), Container( margin: EdgeInsets.only(left: 10.0), child: Text( "Text", style: TextStyle(color: Colors.white, fontSize: 20.0), ), ), ], ), Container( margin: EdgeInsets.only(top: 30.0), child: Text( "Never Stop Thinking...", style: TextStyle(color: Colors.white, fontSize: 30.0), ), ), Container( alignment: Alignment.bottomRight, margin: EdgeInsets.only(top: 30.0), child: Text( "2020-01-10 15:47:35", style: TextStyle(color: Colors.white, fontSize: 14.0), ), ), 53
  • 54.
    ], ), ), ), ), ), ), ); } } Expanded The Expanded componentallows Row, Column, Flex and other sub-components to expand in the direction of their main axis and fill the available space. Similar usage of widget properties in Android 54
  • 55.
    ● Expanded willbe distributed as full as possible in the main axis direction of Row, Column, or Flex ● If Column contains two childs and two widgets are expanded, both share the available vertical space evenly. ● If only one is expanded, the expanded one takes up all the available vertical space. ● If neither is expanded, the available vertical space goes unfilled Example: class ExpandedWidget extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( home: Scaffold( appBar: AppBar(backgroundColor: Colors.pink,title: Text("Expanded Widget"),), body: new Row( children: <Widget>[ new Expanded( flex: 2, child: new Container( child: new Text('Text1', textAlign: TextAlign.center), height: 100, alignment: AlignmentDirectional.center, color: Colors.yellow, ), ), new Expanded( 55
  • 56.
    flex: 1, child: newContainer( child: new Text('Text2', textAlign: TextAlign.center), height: 100, alignment: AlignmentDirectional.center, color: Colors.lightGreen, ), ), new Expanded( flex: 1, child: new Container( child: new Text('Text3', textAlign: TextAlign.center), height: 100, alignment: AlignmentDirectional.center, color: Colors.deepPurple, ), ), ], ), )); } } 56
  • 57.
    Flexible It is actuallyExpanded inheritance Flexible. Use Flexible widgets to Row, Column or Flex provided in the main shaft expands to fill the available space in flexibility (e.g., horizontal filling sub assembly Row or perpendicular filled Column), but Expandeddifferent, Flexible is not required to fill the available space sub components. Flexible The control must be Row, Column or Flex a descendant of Row, the path from the control to its closure , Column or Flex the path must contain only Stateless Widgets or Stateful Widget these, and cannot be other types of widgets (for example Render ObjectWidget) 57
  • 58.
    Center This widget isused to center a Widget within its parent Widget. GestureDetector GestureDetector is a widget that detects gestures. If the child property of GestureDetector is not empty, GestureDetector sets its size to the size of the child. If the child property is empty, it sets its size to the size of the parent component We have different type of gestures, below are few of them ● onTapDown, ● onTapUp, ● onTap, ● onTapCancel, ● onForcePressPeak, ● onForcePressUpdate, ● onForcePressEnd, ● onPanDown, ● onPanStart, ● onPanUpdate, ● onPanEnd, ● onPanCancel, ● onScaleStart, ● onScaleUpdate, ● onScaleEnd 58
  • 59.
    ● onSecondaryTapDown, ● onSecondaryTapUp, ●onSecondaryTapCancel, ● onDoubleTap, ● onLongPress, ● onLongPressStart, ● onLongPressMoveUpdate, ● onLongPressUp, ● onLongPressEnd, Example class GesterDetectorWidget extends StatelessWidget{ GlobalKey<ScaffoldState>_scaffoldstate=GlobalKey(); @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( home: Scaffold( key: _scaffoldstate, appBar: AppBar(title:Text("Card Widget"),backgroundColor: Colors.pink,), body: Container( child: GestureDetector( child: Center( child: Container( color: Colors.pink, child: Padding( padding: const EdgeInsets.all(8.0), child: Text("Gesture Me",style: TextStyle(fontSize: 20,color: Colors.white),), ), )), 59
  • 60.
    onTap: (){ _scaffoldstate.currentState.showSnackBar(SnackBar(content: Text("onTap Event"))); }, onDoubleTap:(){ _scaffoldstate.currentState.showSnackBar(SnackBar(content: Text("onDoubleTap Event"))); }, onLongPress: (){ _scaffoldstate.currentState.showSnackBar(SnackBar(content: Text("onLongPress Event"))); }, ), ) ) ); } } 60
  • 61.
    Positioned This use controlsthe position of the widget, through which he can place a component at will, a bit like an absolute layout Positioned({ Key key, this.left, this.top, this.right, this.bottom, this.width, this.height, @required Widget child, }) 61
  • 62.
    Example class PositionedWidget extendsStatelessWidget{ GlobalKey<ScaffoldState>_scaffoldstate=GlobalKey(); @override Widget build(BuildContext context) { final size = MediaQuery.of(context).size; return MaterialApp( home: Scaffold( key: _scaffoldstate, appBar: AppBar(title:Text("Positioned Widget"),backgroundColor: Colors.pink,), body:Container( width: size.width, height: size.height, child: Stack( children: <Widget>[ Positioned( child: CircleAvatar( backgroundImage: NetworkImage("https://cdn.pixabay.com/photo/2016/10/17/17/4 1/priyanka-chopra-1748248_960_720.jpg"), radius: 80.0, ), right:10, top: 10, ), Positioned( child: CircleAvatar( backgroundImage: NetworkImage("https://cdn.pixabay.com/photo/2016/10/17/17/4 1/priyanka-chopra-1748248_960_720.jpg"), radius: 80.0, ), left: size.width / 2 * 0.8, top: size.height / 2 * 0.7, 62
  • 63.
  • 64.
    SafeArea A widget thatinsets its child by sufficient padding to avoid intrusions by the operating system SafeArea is mainly used to ensure that the view will not be covered by system components, such as the status bar, etc SafeArea Constructor is like below const SafeArea({ Key key, this.left = true, this.top = true, this.right = true, this.bottom = true, this.minimum = EdgeInsets.zero, this.maintainBottomViewPadding = false, @required this.child, }) SingleChildScrollView This Widget is used to show a child Widget even if there is not enough space to view the entirety of the child Widget SingleChildScrollView is similar to scrollview in Android, and it can only contain one child element const SingleChildScrollView({ Key key, this.scrollDirection = Axis.vertical, 64
  • 65.
    this.reverse = false, this.padding, boolprimary, this.physics, this.controller, this.child, this.dragStartBehavior = DragStartBehavior.down, }) key : the unique identifier of the current element (similar to id in Android) scrollDirection : scroll direction, default is vertical reverse : whether to slide in the opposite direction of the reading direction. padding : padding distance primary : Whether to use the default Primary ScrollController in the widget tree. When the sliding direction is vertical (scrollDirection value is Axis.vertical) and the controller is not specified, the primary defaults to true physics : This property accepts a ScrollPhysics object, which determines how the scrollable widget responds to user operations, such as the user continuing to perform an animation after lifting the finger, or how to display it when sliding to the boundary. controller : This property accepts a ScrollController object. The main role of ScrollController is to control the scroll position and listen for scroll events child : child element class SingleChildScrollViewWidget extends StatelessWidget{ var alphabets="ABCDEFGHIJKLMNOPQRSTUVWXYZ"; @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( home: Scaffold( 65
  • 66.
    appBar: AppBar(title:Text("SingleChildScroll Widget"),backgroundColor: Colors.pink,), body:horizontalScroll() )); } horizontalScroll() { return SingleChildScrollView( scrollDirection: Axis.horizontal, child: Center( child: Row( children: alphabets.split("").map((a)=>Padding( padding: const EdgeInsets.all(8.0), child: Text(a,style: TextStyle(fontSize: 20,color: Colors.pink),), )).toList(), ), ), ); } verticalScroll() { return SingleChildScrollView( scrollDirection: Axis.vertical, child: Center( child: Column( children: alphabets.split("").map((a)=>Padding( padding: const EdgeInsets.all(8.0), child: Text(a,style: TextStyle(fontSize: 20,color: Colors.pink),), )).toList(), ), ), ); } } 66
  • 67.
    Vertical Direction HorizontalDirection Themes When we build a Flutter app, we build a root Widget. That Widget usually returns a MaterialApp, which builds the foundations for the app. One of the constructor arguments for MaterialApp is the Theme object. This object specifies the colors to be used in the application’s Widgets. As you can see below the user can pass in Theme data into the MaterialApp constructor using a ThemeData object Change Dynamic theme Find example here http://rrtutors.com/description/27 67
  • 68.
    Scaffold Scaffold is thepage display framework Scaffold has different attributes to handle the Pages appBar: An AppBar displayed at the top of the interface, which is the ActionBar and Toolbar in Android body: the main content widget displayed in the current interface floatingActionButton: FAB defined in paper and ink design, the main function button of the interface persistentFooterButtons: Buttons that are fixed to the bottom, such as OK and Cancel buttons below the dialog box drawer: sidebar control backgroundColor: The background color of the content. The default value is ThemeData.scaffoldBackgroundColor. bottomNavigationBar: the navigation bar displayed at the bottom of the page resizeToAvoidBottomPadding: similar to android: windowSoftInputMode = ”adjustResize” in Android, controls whether the content body of the interface is rearranged to avoid the bottom being covered, for example, when the keyboard is displayed, the re-layout is to avoid covering the content with the keyboard. The default value is true class ScffoldHomePage extends StatefulWidget { @override State<StatefulWidget> createState() { return ScffoldHomePageState(); } } 68
  • 69.
    class ScffoldHomePageState extendsState<ScffoldHomePage> { num index =0; List <Widget> pageWidgetList =[ Home(), SearchScreen(), ProfileScreen(), ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("HomePage"), backgroundColor: Colors.pink, ), body:pageWidgetList[index], floatingActionButton: FloatingActionButton( child: Text("++"), onPressed: () { }, tooltip: "Click tooltips", backgroundColor: Colors.pink, focusColor: Colors.green, hoverColor: Colors.purpleAccent, 69
  • 70.
    splashColor: Colors.deepPurple, foregroundColor: Colors.white, elevation:0.0, highlightElevation: 20.0, ), floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, persistentFooterButtons: <Widget>[ Text( "1", style: TextStyle(color: Colors.blue), ), Text("2"), Text("3"), Text("4"), Text("5"), ], drawer: Container( color: Colors.grey, width: 120, child: FlatButton( child: Text("Close Left Swipe"), onPressed: () { Navigator.of(context).pop(); }, ), ), endDrawer: Container( color: Colors.orange, width: 200, 70
  • 71.
    height: 800, child: FlatButton( child:Text("Close Right Swipe",style: TextStyle(color: Colors.white),), onPressed: () { Navigator.of(context).pop(); }, ), ), bottomNavigationBar:new BottomNavigationBar( backgroundColor: Colors.pink, items: <BottomNavigationBarItem>[ BottomNavigationBarItem(icon:Icon(Icons.home,color: index==0?Colors.white:Colors.white,),title: Text("Home",style: TextStyle(color: index==0?Colors.white:Colors.white),) ), BottomNavigationBarItem(icon:Icon(Icons.search,color: index==1?Colors.white:Colors.white,),title: Text("Search",style: TextStyle(color: index==1?Colors.white:Colors.white),) ), BottomNavigationBarItem(icon:Icon(Icons.people,color: index==2?Colors.white:Colors.white,),title: Text("Account",style: TextStyle(color: index==2?Colors.white:Colors.white),) ), ], onTap: (flag) { print("flag $flag"); index = flag; setState(() {}); }, currentIndex: index, ) , ); } } 71
  • 72.
  • 73.
    Dialogs A material designdialog, Dialogs are temporary windows that appear as overlays over the existing application Show dialog is simple showDialog(context: context, builder: (context) => Center(child: Text("Dialog"))); In flutter we have multiple ways to show dialogs 1. Alert Dialog 2. Custom Dialog 3. Full-Screen Dialog Simple AlertDialog showDialog( context: context, builder: (BuildContext context){ return AlertDialog( title: Text("Alert Dialog"), content: Text("Dialog Content"), ); } ) This is a simple alert dialog with title and message. We can also add buttons to handle the events 73
  • 74.
    Add buttons toAlert Dialogs This widget, there is a parameter called action. It accepts an array of widgets and we can provide multiple buttons to that. Those Buttons will appear in the bottom right corner of the dialog actions:[ FlatButton( child: Text("Close"), ) ] How to close Dialog We can close the Displayed Dialog by calling the Navigator.of(context).pop(); FlatButton( child: Text("Close"), onPressed: (){ Navigator.of(context).pop(); }, ) Example class MyDialog extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Dialog"),backgroundColor: Colors.pink,), body: Center( 74
  • 75.
    child: FlatButton( onPressed: (){ showMyDialog(context); }, child: Text( 'Show Me', style: TextStyle( fontSize: 16.0, fontWeight: FontWeight.bold, color: Colors.white), ), color: Colors.pink, ), ), ); } void showMyDialog(BuildContext context) { showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return Dialog( child: _contentWidget(context), insetAnimationCurve: Curves.fastOutSlowIn, insetAnimationDuration: Duration(milliseconds: 100), shape: RoundedRectangleBorder( borderRadius: BorderRadius.all( Radius.circular(8.0), ), ),); }); } Widget _contentWidget(BuildContext context) { return Center( widthFactor: 2.0, heightFactor: 1.0, child: Container( width: 300.0, height: 200.0, 75
  • 76.
    color: Colors.white, child: Column( children:<Widget>[ Expanded( child: Container( padding: EdgeInsets.only(top: 5.0), child: Text('This is title',style: TextStyle(color: Colors.black,fontWeight: FontWeight.bold,fontSize: 22.0),),), flex: 1, ), Expanded( child: Container( alignment: Alignment.topLeft, margin: EdgeInsets.all(20.0), child: Text('Text Message to display the Dialog in Flutter',style: TextStyle(fontSize: 18.0,color: Colors.black),), ), flex: 3, ), Expanded( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ RaisedButton(onPressed: (){ Navigator.of(context).pop(); }, child: Text('Continue',style: TextStyle(color: Colors.white)),color: Colors.pink,), FlatButton(onPressed: (){ Navigator.of(context).pop(); }, child: Text('Cancel',style: TextStyle(color: Colors.pink),)), ], ), flex: 2, ), ], 76
  • 77.
  • 78.
    ExpansionPanelList & ExpansionPanel Thesetwo widgets are designed to work together to present a list of expandable panels to the user We have to manage the state of what was expanded / collapsed and rebuild the ExpansionPanelList & ExpansionPanels everytime the state changes ExpansionPanel Shrink the panel. It has a title and a body that can be expanded or collapsed. The body of the panel is only visible when expanded. The shrink panel is only used as a child of ExpansionPanelList. Example implementation, please use ExpansionPanelList ExpansionPanel({ @required this.headerBuilder, @required this.body, this.isExpanded = false, this.canTapOnHeader = false, }) ExpansionPanelList A material expansion panel list that lays out its children and animates expansions Lays out the child ExpansionPanels const ExpansionPanelList({ Key key, this.children = const <ExpansionPanel>[], this.expansionCallback, this.animationDuration = kThemeAnimationDuration, }) 78
  • 79.
    There are onlythree parameters that we need to use: children: Needless to say, it is ExpansionPanel expansionCallback: expansion callback, here will return the index of the click animationDuration: the duration of the animation Example: class ExpansionWidget extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return ExpansionWidgetState(); } } class ExpansionWidgetState extends State<ExpansionWidget>{ List<bool>listExpans=List(); @override void initState() { // TODO: implement initState super.initState(); listExpans.add(false); listExpans.add(false); } @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( backgroundColor: Colors.grey, appBar: AppBar(title: Text("Expansionpanel"),backgroundColor: Colors.pink,), body: SingleChildScrollView( child: Container( 79
  • 80.
    alignment: Alignment.center, child: Column( children:<Widget>[ ExpansionPanelList( children : <ExpansionPanel>[ ExpansionPanel( headerBuilder:(context, isExpanded){ return ListTile( title: Text('Try Expansion 1'), ); }, body: Padding( padding: EdgeInsets.fromLTRB(15, 0, 15, 15), child: ListBody( children: <Widget>[ Card( margin:EdgeInsets.fromLTRB(0, 0, 0, 10), child: Padding(padding: EdgeInsets.all(8),child: Text('Content 1'),), ), Card( margin:EdgeInsets.fromLTRB(0, 0, 0, 10), child: Padding(padding: EdgeInsets.all(8),child: Text('Content 2'),), ), Card( margin:EdgeInsets.fromLTRB(0, 0, 0, 10), child: Padding(padding: EdgeInsets.all(8),child: Text('Content 3'),), ), Card( margin:EdgeInsets.fromLTRB(0, 0, 0, 10), child: Padding(padding: EdgeInsets.all(8),child: Text('Content 4'),), ), Card( margin:EdgeInsets.fromLTRB(0, 0, 0, 10), 80
  • 81.
    child: Padding(padding: EdgeInsets.all(8),child: Text('Content5'),), ), ], ), ), isExpanded: listExpans[0], canTapOnHeader: true, ), ExpansionPanel( headerBuilder:(context, isExpanded){ return ListTile( title: Text('Try Expansion 2 '), ); }, body: Padding( padding: EdgeInsets.fromLTRB(15, 0, 15, 15), child: ListBody( children: <Widget>[ Card( margin:EdgeInsets.fromLTRB(0, 0, 0, 10), child: Padding(padding: EdgeInsets.all(8),child: Text('Content 1'),), ), Card( margin:EdgeInsets.fromLTRB(0, 0, 0, 10), child: Padding(padding: EdgeInsets.all(8),child: Text('Content 2'),), ), Card( margin:EdgeInsets.fromLTRB(0, 0, 0, 10), child: Padding(padding: EdgeInsets.all(8),child: Text('Content 3'),), ), Card( margin:EdgeInsets.fromLTRB(0, 0, 0, 10), 81
  • 82.
    child: Padding(padding: EdgeInsets.all(8),child: Text('Content4'),), ), Card( margin:EdgeInsets.fromLTRB(0, 0, 0, 10), child: Padding(padding: EdgeInsets.all(8),child: Text('Content 5'),), ), ], ), ), isExpanded: listExpans[1], canTapOnHeader: true, ), ], expansionCallback:(panelIndex, isExpanded){ setState(() { listExpans[panelIndex] = !isExpanded; }); }, animationDuration : kThemeAnimationDuration, ), ], ), ), ) ); } } 82
  • 83.
    GridView GridView displays thevalues of a data source in a table where each column represents a field and each row represents a record Constructors GridView.builder() GridView.count() GridView.custom() GridView.extent() 83
  • 84.
    GridView.builder({ Key key, Axis scrollDirection= Axis.vertical, bool reverse = false, ScrollController controller, bool primary, ScrollPhysics physics, bool shrinkWrap = false, EdgeInsetsGeometry padding, @required this.gridDelegate, @required IndexedWidgetBuilder itemBuilder, int itemCount, bool addAutomaticKeepAlives = true, bool addRepaintBoundaries = true, bool addSemanticIndexes = true, int semanticChildCount, }) Example class GridViewWidget extends StatelessWidget { List list = new List(); GridViewWidget() { for (int i = 1; i < 20; i++) { int j = (i % 9) + 1; var temp = { "imageurl": "https://cdn.pixabay.com/photo/2016/10/17/17/41/priyan ka-chopra-1748248_960_720.jpg", "title": "Image $i" }; list.add(temp); } } @override Widget build(BuildContext context) { 84
  • 85.
    // TODO: implementbuild return Scaffold( appBar: AppBar( title: Text("GridView"), backgroundColor: Colors.pink, ), body: GridView.builder( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, mainAxisSpacing: 5, crossAxisSpacing: 5, ), itemCount: list.length, itemBuilder: (BuildContext context, int index) { return Container( decoration: BoxDecoration( border: Border.all( color: Colors.red, width: 2, )), child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[ Image.network( list[index]['imageurl'], fit: BoxFit.cover, ), Expanded(child: Text(list[index]['title'])), ], ), ); })); } } 85
  • 86.
    PopupMenu Why use thisPopupMenu? Because most message interfaces have a setting option in the upper right corner, and this option is most commonly implemented through PopupMenu. Look at the effect map 86
  • 87.
    class PopupWidget extendsStatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return PopupWidgetState(); } } class PopupWidgetState extends State<PopupWidget> { int _value=1; @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text("Popup Window"),backgroundColor: Colors.pink, actions: <Widget>[ _NomalPopMenu() ],), body: Container( child: Center( child: Container( decoration:ShapeDecoration(shape: OutlineInputBorder( )), width: 200, height: 40, child: Center(child: Text("Value selected $_value",style: TextStyle(color: Colors.pink,fontSize: 20),)), ), ), ), ); 87
  • 88.
    } Widget _NomalPopMenu() { returnnew PopupMenuButton<int>( itemBuilder: (BuildContext context) => <PopupMenuItem<int>>[ new PopupMenuItem<int>( value: 1, child: new Text('Item One')), new PopupMenuItem<int>( value: 2, child: new Text('Item Two')), new PopupMenuItem<int>( value: 3, child: new Text('Item Three')), new PopupMenuItem<int>( value: 4, child: new Text('I am Item Four')) ], onSelected: (int value) { setState(() { _value = value; }); }); } } Checked Widgets CheckBox Checkbox is a checkbox component, usually used for setting options Attributes ● activeColor : Color -the color when active. ● onChanged : ValueChanged -fired when changed. ● tristate: bool -If true, the value of the checkbox can be true, false or null. 88
  • 89.
    ● value :bool -the value of the checkbox CheckboxListTile CheckboxListTile is an upper-level package of Checkbox. Its appearance is to provide a selection component similar to a setting page, which can set icons and text Radio Radio is used for single selection options Attributes ● Value of value radio ● groupValue radio The value of the group. Value == groupValue is selected. ● onChanged callback when the value changes ● activeColor when selected Example class CheckWidgets extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return CheckWidgetState(); } } class Course{ Course(this.title,this.price,this.courseCheck); 89
  • 90.
    bool courseCheck; String title; doubleprice; } class CheckWidgetState extends State<CheckWidgets> { int priceCheck=0; String doller_rupee="Rs"; List<Course>listCourse=List(); @override void initState() { // TODO: implement initState super.initState(); listCourse.add(Course("Course 1",699,false)); listCourse.add(Course("Course 2",693,false)); } @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text("Checked Widgets"),backgroundColor: Colors.pink,), body: Container( margin: EdgeInsets.all(10), child: Column( children: <Widget>[ Card( child: Padding( padding: const EdgeInsets.all(8.0), child: Column( children: <Widget>[ Row( 90
  • 91.
    children: <Widget>[ Text("Price in",style: TextStyle(color: Colors.pink,fontSize: 22),), ], ), Row( children: <Widget>[ Row( children: <Widget>[ Radio( value: 0, activeColor: Colors.pink, groupValue: priceCheck, onChanged: (newValue) { setState(() { priceCheck = newValue; doller_rupee="Rs"; }); } ), Text('Rupee') ], ), Row( children: <Widget>[ Radio( value: 1, activeColor: Colors.pink, groupValue: priceCheck, onChanged: (newValue) { setState(() { priceCheck = newValue; doller_rupee="$"; }); } ), Text('Dollar') 91
  • 92.
    ], ), ], ), ], ), ), ) , Card( child: Padding( padding:const EdgeInsets.all(8.0), child: Column( children: <Widget>[ Row( children: <Widget>[ Text("Select Course",style: TextStyle(color: Colors.pink,fontSize: 24),), ], ), ListView.builder( shrinkWrap: true, itemCount: listCourse.length, itemBuilder: (ctx,pos){ return Column( children: <Widget>[ CheckboxListTile( subtitle: Text(" $doller_rupee ${listCourse[pos].price}",style: TextStyle(fontSize: 16,color: Colors.green),), title: Text(listCourse[pos].title,style: TextStyle(fontSize: 22,color: Colors.black),), checkColor:Colors.white, activeColor:Colors.pink, value: this.listCourse[pos].courseCheck, onChanged: (bool value) { setState(() { listCourse[pos].courseCheck = 92
  • 93.
  • 94.
  • 95.
    TabBar TabBarView TabBar isa row of horizontal tabs, you can switch back and forth, the effect map TabBar is generally used together with TabBarView. TabBarView is used to select different TabBars. TabBarView displays the corresponding View TabBarView property description class TabBarDemo extends StatefulWidget { @override State<StatefulWidget> createState() => _TabBar(); } class _TabBar extends State<TabBarDemo> { final List<String> _tabValues = [ 'Tab1', 'Tab2', 'Tab3', 'Tab4', 'Tab5', 'Tab6', 'Tab7', 'Tab8', ]; TabController _controller; @override void initState() { super.initState(); _controller = TabController( length: _tabValues.length, 95
  • 96.
    vsync: ScrollableState(), ); } @override Widget build(BuildContextcontext) { // TODO: implement build return Scaffold( appBar: AppBar( backgroundColor: Colors.pink, title: Text('TabBar'), bottom: TabBar( tabs: _tabValues.map((f) { return Text(f,style: TextStyle(fontSize: 20),); }).toList(), controller: _controller, indicatorColor: Colors.grey, isScrollable: true, labelColor: Colors.white, unselectedLabelColor: Colors.white54, indicatorWeight: 5.0, labelStyle: TextStyle(height: 2), ), ), body: TabBarView( controller: _controller, children: _tabValues.map((f) { return Center( child: Text(f,style: TextStyle(fontSize: 80,color: Colors.pink),), ); }).toList(), ), ); } } 96
  • 97.
    Table A widget thatuses the table layout algorithm for its children The height of each row of the table is determined by its content, and the width of each column is controlled individually by the columnWidths property 97
  • 98.
    class TableWidget extendsStatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text("Table Widget"),backgroundColor: Colors.pink,), body: Container( child: Padding( padding: const EdgeInsets.all(8.0), child: Card( child: Table( columnWidths: const <int, TableColumnWidth>{ 0: FixedColumnWidth(50.0), 1: FixedColumnWidth(100.0), 2: FixedColumnWidth(50.0), 3: FixedColumnWidth(100.0), }, border: TableBorder.all(color: Colors.pink, width: 1.0, style: BorderStyle.solid), children: const <TableRow>[ TableRow( children: <Widget>[ Center(child: Text('A1',style: TextStyle(fontSize: 18),)), Center(child: Text('B1',style: TextStyle(fontSize: 18),)), Center(child: Text('C1',style: TextStyle(fontSize: 18),)), Center(child: Text('D1',style: TextStyle(fontSize: 18),)), ], ), TableRow( children: <Widget>[ Center(child: Text('A2',style: TextStyle(fontSize: 18),)), Center(child: Text('B2',style: TextStyle(fontSize: 18),)), Center(child: Text('C2',style: TextStyle(fontSize: 18),)), Center(child: Text('D2',style: TextStyle(fontSize: 18),)), ], 98
  • 99.
    ), TableRow( children: <Widget>[ Center(child: Text('A3',style:TextStyle(fontSize: 18),)), Center(child: Text('B3',style: TextStyle(fontSize: 18),)), Center(child: Text('C3',style: TextStyle(fontSize: 18),)), Center(child: Text('D3',style: TextStyle(fontSize: 18),)), ], ), ], ), ), ) , ), ); } } 99
  • 100.
  • 101.
    Future and FutureBuilder ToMake the Asynchronous operation will use Future Uses future.then get future value and catch future exception Combined async,await future.whenComplete future.timeout import 'dart:async'; Future<String> testFuture() { // throw new Error(); return Future.value('success'); // return Future.error('error'); } main() { testFuture().then((s) { print(s); }, onError: (e) { print('onError:'); print(e); }).catchError((e) { print('catchError:'); print(e); }); } Sometimes we need to Futuredo something at the end, and we know then().catchError()the pattern is similar try-catch, try-catch there is a finally code block, and future.whenCompletethat Future Is finally 101
  • 102.
    FutureBuilder FutureBuilder combines asynchronousoperations and asynchronous UI updates. Through it we can update the results of network requests, database reads, etc. future: Future object represents the asynchronous calculation currently connected to this builder; initialData: Indicates the initialization data of a non-empty Future before completion; builderA return function of type AsyncWidgetBuilder is a function that builds a widget based on asynchronous interaction Properties connectionState -Enumeration of ConnectionState, which indicates the connection status with asynchronous calculation. ConnectionState has four values: none, waiting, active, and done; data -Asynchronous calculation of the latest data received; error -Asynchronously calculate the latest error object received; Example class FutureWidget extends StatefulWidget { String computeListOfTimestamps(int count) { StringBuffer sb = new StringBuffer(); var random = Random(); for (int i = 0; i < 2000; i++) { sb.writeln("Random Number : ${random.nextInt(2000)}"); sb.writeln(""); } return sb.toString(); } 102
  • 103.
    Future<String> createFutureCalculation(int count){ return new Future(() { return computeListOfTimestamps(count); },); } @override FutureWidgetState createState() => new FutureWidgetState(); } class FutureWidgetState extends State<FutureWidget> { bool _showCalculation = false; bool isLoading = false; GlobalKey<ScaffoldState>_scaffoldKey=GlobalKey(); void _onInvokeFuturePressed() { setState(() { _showCalculation = !_showCalculation; isLoading=true; }); } @override Widget build(BuildContext context) { Widget child = _showCalculation ? FutureBuilder( future: widget.createFutureCalculation(1000), builder: (BuildContext context, AsyncSnapshot snapshot) { return Expanded( child: SingleChildScrollView( child: (snapshot.data == null)?Center(child: CircularProgressIndicator()):Text( '${snapshot.data == null ? "" : snapshot.data}', 103
  • 104.
    style: TextStyle(fontSize: 20.0)))); }) :Text('Press Refresh to load Data'); return new Scaffold( key: _scaffoldKey, appBar: new AppBar( backgroundColor: Colors.pink, title: new Text("Future Builder "), ), body: new Center( child: new Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[child])), floatingActionButton: new FloatingActionButton( backgroundColor: Colors.pink, onPressed: _onInvokeFuturePressed, tooltip: 'Call Future', child: new Icon(Icons.refresh), ), // This trailing comma makes auto-formatting nicer for build ); } } 104
  • 105.
    StreamBuilder StreamBuilder is similarto FutureBuilder, provides the ability to acquire asynchronous data and update ui Stream is an event stream, which is similar to RxJava. It allows us to emit an event from one end and listen to changes in the event from the other 105
  • 106.
    end. Through Streamwe can design reactive code based on event flow on Flutter logic Stream is not flutter widget, it provided by Dart Stream is an abstract interface StreamBuilder Constructor const StreamBuilder({ Key key, this.initialData, Stream<T> stream, @required this.builder, }) Example class StreamBuilderWidget extends StatelessWidget{ var index = 0; StreamSubscription<String> subscription; var streamController = StreamController<String>(); //To Emit the stream StreamSink<String> get streamSink => streamController.sink; Stream<String> get streamData => streamController.stream; StreamBuilderWidgetState() { streamSink.add("0"); } 106
  • 107.
    void onFloatActionButtonPress() { streamSink.add(index.toString()); index++; } @override Widgetbuild(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text("Stream Builder"),backgroundColor: Colors.pink,), body: Center( child: StreamBuilder<String>( stream: streamData, builder: (BuildContext context, AsyncSnapshot<String> snapshot) { return Text('Result: ${snapshot.data}'); } ) ), floatingActionButton: FloatingActionButton( onPressed: onFloatActionButtonPress, child: Icon(Icons.add)) ); } } 107
  • 108.
    Navigation To Implement pagenavigation in Flutter we need to use two classes Navigator and Route. The Navigator is responsible for the stack structure processing of the page and the route processing of the Route complex page The Navigator used the stack structure to manage pages. When a page needs to be added, the stack method is used, when we need to exit a page use the pop-out method Push and pop will handle all stack elements Let’s Start Code Create Two Pages First, we will create two pages, each of which contains a button. Clicking the button on the first page will jump to the second page, and clicking the button on the second page will return to the first page class FirstPage extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text(" First Page"), backgroundColor: Colors.pink,), body: Center( child: RaisedButton( onPressed: (){ 108
  • 109.
    }, child: Text("Second Page",style: TextStyle(color: Colors.white),), color: Colors.pink, ), ), ); } } class SeconPage extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text(" Second Page"), backgroundColor: Colors.pink,), body: Center( child: RaisedButton( onPressed: (){ }, child: Text("Navigate to First Page", style: TextStyle(color: Colors.white),), color: Colors.pink, ), ), ); } } 109
  • 110.
    Now let’s jumpto second page In order to jump to the second page, we will use the Navigator.push method. This push method will add one Route to the routing heap managed by Navigator. But the Push method requires one Route, but Route where does it come from? We can create our own Route, or use it MaterialPageRoute. MaterialPageRoute Conveniently, it will jump to a new page using platform-specific animations. In the FirstScreen Widget build method, update the onPressedcallback onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (context) => SeconPage()), ); }, Return to the first Now that we are on the second screen, how do we close it and return to the first screen? Use the Navigator.pop method! pop Method to remove the current one from the routing stack managed by Navigator Route. In this part, update SecondScreenthe onPressedcallback in the widget onPressed: () { Navigator.pop(context); }, 110
  • 111.
    Complete code class NavigationDemoextends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return FirstPage(); } } class FirstPage extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text(" First Page"), backgroundColor: Colors.pink,), body: Center( child: RaisedButton( onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (context) => SeconPage()), ); }, child: Text("Second Page", style: TextStyle(color: Colors.white),), color: Colors.pink, ), ), ); } } class SeconPage extends StatelessWidget{ @override 111
  • 112.
    Widget build(BuildContext context){ // TODO: implement build return Scaffold( appBar: AppBar(title: Text(" Second Page"), backgroundColor: Colors.pink,), body: Center( child: RaisedButton( onPressed: () { Navigator.pop(context); }, child: Text("Navigate to First Page", style: TextStyle(color: Colors.white),), color: Colors.pink, ), ), ); } } 112
  • 113.
    Routing table (NamedRoutes) Flutter also provided Navigation with NamedRoutes The simple way is to define all routes in the runApp, so that centralized management is possible, which is also a very recommended approach A MaterialApp is the easiest way to set it up, and the MaterialApp's home becomes the route at the bottom of the navigator stack. To push a new route on the stack, you can create a MaterialPageRoute instance with builder capabilities class NamedRoutes extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build 113
  • 114.
    return MaterialApp( home: HomePage(), routes:<String,WidgetBuilder>{ '/f':(BuildContext ctx)=> FisrtPage(), '/s':(BuildContext ctx)=> SecondPage(), '/t':(BuildContext ctx)=> ThirdPage(), }, ); } } class HomePage extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text("HomePage"),backgroundColor: Colors.pink,), body: Center( child: Column( mainAxisSize: MainAxisSize.min, children: <Widget>[ RaisedButton( color: Colors.pink, onPressed: (){ Navigator.pushNamed(context, "/f"); },child: Text("FirstPage",style: TextStyle(color: Colors.white)),), RaisedButton( color: Colors.pink, onPressed: (){ Navigator.pushNamed(context, "/s"); },child: Text("SecondPage",style: TextStyle(color: Colors.white)),), 114
  • 115.
    RaisedButton( color: Colors.pink, onPressed: (){ Navigator.pushNamed(context,"/t"); },child: Text("ThirdPage",style: TextStyle(color: Colors.white),),), ], ), ), ); } } class FisrtPage extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text("FirstPage"),backgroundColor: Colors.pink,), body: Center( child: Column( children: <Widget>[ RaisedButton(onPressed: (){Navigator.pop(context,"From First page");},child: Text("FirstPage"),) ], ), ), ); } } class SecondPage extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build 115
  • 116.
    return Scaffold( appBar: AppBar(title:Text("SecondPage"),backgroundColor: Colors.pink,), body: Center( child: Column( children: <Widget>[ RaisedButton(onPressed: (){Navigator.pop(context);},child: Text("SecondPage"),) ], ), ), ); } } class ThirdPage extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text("ThirdPage"),backgroundColor: Colors.pink,), body: Center( child: Column( children: <Widget>[ RaisedButton(onPressed: (){Navigator.pop(context);},child: Text("ThirdPage"),) ], ), ), ); } } To get the result from called page by 116
  • 117.
    onPressed: (){ Navigator.pushNamed(context, "/f").then((value){ print(value); }); } Afterpop the screen the then() method will execute and print the result. Navigation switch animation The default navigation switch animation is a pop-up process from bottom to top on Android, and a pan-to-left process from iOS In order to unify, you can customize a route, including the color and behavior of the modal barrier of the route, and the animation conversion of other aspects of the route You can find the example of Navigation Screen animation at http://rrtutors.com/description/53 117
  • 118.
    Form & FormFields A form is an area that contains form elements. Form elements allow users to enter content, such as text fields, drop-down lists, radio boxes, check boxes, and so on. Common application scenarios are: login, registration, input information, etc. There are two important components in the form, one is the Form component for the entire form submission, and the other is the TextFormField component for user input Form Constructor const Form({ Key key, @required this.child, this.autovalidate = false, this.onWillPop, this.onChanged, }) The Form object gives the following methods: reset to reset fields. save to save fields. validate to validate, returning a true if the form fields are valid, false if one or more are invalid Form State The Form object stores input state data from child TextFormFields but not other field types like Checkboxes, DropdownButtons, Radios, Switches. So, 118
  • 119.
    if we wantform to work with those other types of fields, we need to store the state of those items. If we take a look a look at the example we will see that these fields are stored as state in the Stateful Widget Form Validation As mentioned earlier, the Form class has an autovalidate constructor argument. If this argument is set to true, the framework invokes validation as data is input. If this argument is set to false, the framework will not invoke validation until the validate method is invoked Example With Login Page class FormWidget extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return _LoginPageState(); } } class _LoginPageState extends State<FormWidget> { GlobalKey<FormState> loginKey = GlobalKey<FormState>(); String userName; String password; void login() { 119
  • 120.
    var loginForm =loginKey.currentState; if (loginForm.validate()) { loginForm.save(); print('userName:' + userName + ',password:' + password); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Form Fileds'), backgroundColor: Colors.pink, centerTitle: true, ), body: SingleChildScrollView( child: Column( children: <Widget>[ Container( padding: EdgeInsets.all(16), child: Form( key: loginKey, autovalidate: true, child: Column( children: <Widget>[ TextFormField( decoration: InputDecoration( labelText: 'User Name', hintText: "Enter User Name", hintStyle: TextStyle( color: Colors.grey, fontSize: 13, ), prefixIcon: Icon(Icons.person,color: Colors.pink,), 120
  • 121.
    ), validator: (value) { returnvalue.trim().length > 0 ? null : "Please enter valid user name"; }, onSaved: (value) { userName = value; }, onFieldSubmitted: (value) {}, ), TextFormField( decoration: InputDecoration( labelText: 'Password', hintText: 'Enter password', hintStyle: TextStyle( color: Colors.grey, fontSize: 13, ), prefixIcon: Icon(Icons.lock,color: Colors.pink), ), obscureText: true, validator: (value) { return value.length < 6 ? 'Password should be min 6 characters' : null; }, onSaved: (value) { password = value; }, ), ], ), 121
  • 122.
    onChanged: () { print("onChanged"); }, ), ), Container( padding:EdgeInsets.all(16), child: Row( children: <Widget>[ Expanded( child: RaisedButton( padding: EdgeInsets.all(15), child: Text( "Login", style: TextStyle(fontSize: 18), ), textColor: Colors.white, color: Colors.pink, onPressed: login, ), ), ], ), ) ], ), ), ); } } 122
  • 123.
    Input Decoration Themes Byusing this Input Decoration themes we can change the style for form fields as we like ThemeData( scaffoldBackgroundColor: Color(0xFF000000), appBarTheme: AppBarTheme( color: Colors.white, textTheme: TextTheme( title: TextStyle(fontSize: 22, color: Colors.white), ) 123
  • 124.
    ), brightness: Brightness.dark, backgroundColor: Color(0xFF000000), accentColor:Colors.white, accentIconTheme: IconThemeData(color: Colors.black), dividerColor: Colors.black54, accentTextTheme:TextTheme( headline: TextStyle(fontSize: 35, color: Colors.white), title: TextStyle(fontSize: 35, color: Colors.white), body1: TextStyle(fontSize: 35, color: Colors.white), subtitle: TextStyle(fontSize: 18, color: Colors.white), display1: TextStyle(fontSize: 35, color: Colors.white) ), inputDecorationTheme: InputDecorationTheme( focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.white), borderRadius: BorderRadius.all(Radius.circular(10))), enabledBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.white), borderRadius: BorderRadius.all(Radius.circular(10))), errorBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.white), borderRadius: BorderRadius.all(Radius.circular(10))), focusedErrorBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.white), borderRadius: BorderRadius.all(Radius.circular(10))), border: OutlineInputBorder( borderSide: BorderSide(color: Colors.white), borderRadius: BorderRadius.all(Radius.circular(10)), ), labelStyle: TextStyle( color: Colors.white, fontSize: 12.0 ), ), buttonTheme: ButtonThemeData( shape: new RoundedRectangleBorder( 124
  • 125.
    borderRadius: BorderRadius.all(Radius.circular(10.0)), side: BorderSide(color:Colors.white)), ) ); We can change the Theme of application dynamically, you can find sample at http://rrtutors.com/description/27 125
  • 126.
    Networking & JSONand Serialization Now a days every mobile application communicate with remote server on Networking Flutter provides a sky_enginedevelopment kit in the Flutter engine , which contains a _http library that contains various operation classes related to encapsulated http requests. In this article, we will introduce _http the use of related operation classes and the use _httpof three-party dio network libraries based on encapsulation HTTP Introduction The Hypertext Transfer Protocol (HTTP) is designed to enable communications between clients and servers. HTTP works as a request-response protocol between a client and server. A protocol describes how machines communicate with each other using messages. A protocol defines the format of these messages HTTPClient Make HTTPRequest http support is located dart:io, so to create an HTTP client, we need to add an import import 'dart:io'; var httpClient = new HttpClient(); 126
  • 127.
    HTTP API usesDart Futures in the return value . We recommend using the async/ await syntax to call the AP Involve below steps to handle the HttpClient ● Create client ● Construct Uri ● Make a request, wait for a request, and you can also configure request headers and body ● Close the request and wait for a response ● Decode the content of the response get() async { var httpClient = new HttpClient(); var uri = new Uri.http( 'domain', 'path to api', params in map object); var request = await httpClient.getUrl(uri); var response = await request.close(); var responseBody = await response.transform(UTF8.decoder).join(); } Decode and encode JSON The dart:convertlibrary makes it easy to decode and encode JSON. Decode a simple JSON string and parse the response into a Map Map data = JSON.decode(responseBody); String name=mapNews[0]['name'] 127
  • 128.
    Check Example class NetwotkHTTPextends StatefulWidget { NetwotkHTTP({Key key}) : super(key: key); @override _NetwotkHTTPState createState() => new _NetwotkHTTPState(); } class _NetwotkHTTPState extends State<NetwotkHTTP> { Map<int,dynamic>mapNews=Map(); @override void initState() { // TODO: implement initState super.initState(); _getNewsData(); } _getNewsData() async { var url='https://newsapi.org/v2/sources?apiKey=API_KEY&p age=1'; //var url = 'https://httpbin.org/ip'; var httpClient = new HttpClient(); var listNewsdata=""; try { var request = await httpClient.getUrl(Uri.parse(url)); var response = await request.close(); if (response.statusCode == HttpStatus.OK) { var json = await response.transform(utf8.decoder).join(); var data = jsonDecode(json); 128
  • 129.
    List<dynamic>hm=data['sources'] as List; setState((){ mapNews=hm.asMap(); print("errorr SetState"+mapNews.toString()); }); } else { print("errorr "); listNewsdata = 'Error Resposne :nHttp status ${response.statusCode}'; } } catch (exception) { print("errorr $exception"); listNewsdata = 'Failed getting News Data $exception'; } // If the widget was removed from the tree while the message was in flight, // we want to discard the reply rather than calling setState to update our // non-existent appearance. if (!mounted) return; } @override Widget build(BuildContext context) { var spacer = new SizedBox(height: 32.0); return new Scaffold( appBar: AppBar(title: Text("News API WITH HTTP CLIENT"),backgroundColor: Colors.pink,), body: SingleChildScrollView( 129
  • 130.
    child: new Center( child:new Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ (mapNews.length==0)?CircularProgressIndicator():Text(""), ListView.builder( shrinkWrap: true, primary: false, itemCount: mapNews.length, itemBuilder: (ctx,pos){ return Card( elevation: 5, child: Container( margin: EdgeInsets.all(5), padding: EdgeInsets.all(5), child: InkWell( onTap: (){}, child: Row( children: <Widget>[ Container( height: 50, width: 50, decoration: ShapeDecoration(shape: CircleBorder(),color: Colors.pink), child: Center(child: Text(mapNews[pos]['name'].substring(0,1).toUpperCase(),style: TextStyle(fontSize: 30,color: Colors.white),)), ), SizedBox(width:15,), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Text(mapNews[pos]['name'],style: TextStyle(fontSize: 18,color: Colors.black),), SizedBox(height:5,), Text(mapNews[pos]['description'],style: 130
  • 131.
    TextStyle(fontSize: 12,color: Colors.grey[800]),maxLines:3,), ], ), ) ], ), ), ) ); }) ], ), ), ), ); } } HTTP Library http Library http.dartfile package HttpClientclass is our common network requests the operation class, which is an abstract class, the specific operation by the http_impl.dart document _HttpClient class that implements the class encapsulates http request various methods, including get, post, put, delete, patchAnd head wait for the request Let's look at the usage method through the following example 131
  • 132.
    In this exampleusing the NewsAPI to fetch the news headlines. News API is a simple HTTP REST API for searching and retrieving live articles from all over the web To call api in Flutter we need to add http: ^0.12.0+2 dependencies in the pubspec.yaml file The above same example with http library static Future callNewsCAtegory() async{ var url="API_KEY"; return get('https://newsapi.org/v2/sources?apiKey=$url&page=1') ; } The above method will return the response data, that we are handle in below code fetchNews(){ var callNews = NewsCategoryModel.callNewsCAtegory(); callNews.then((data){ var response=json.decode(data.body ); print(response); var listNewsdata=response['sources']as List; setState(() { listNews=listNewsdata.map<NewsCategoryModel>((model) =>NewsCategoryModel.fromJson(model)).toList(); listNewsAll.clear(); listNewsAll.addAll(listNews); }); 132
  • 133.
    },onError: (error){ print("Result Error$error"); } ); } POST Method Map<String, String> queryParameters = {'key': 'value', 'key': 'value'}; Map<String, String> header = {'key': 'value'}; post("API_URL",body: queryParameters,headers: header); JSON Parsing After getting the response from the API we need to parse the data, for this we are using the import 'dart:convert' library var callNews = NewsCategoryModel.callNewsCAtegory(); callNews.then((data){ var response=json.decode(data.body ); print(response); var listNewsdata=response['sources']as List Var listNews=listNewsdata.map<NewsCategoryModel>((model)=>Ne wsCategoryModel.fromJson(model)).toList(); The NewsCategoryModel class like below 133
  • 134.
    class NewsCategoryModel { Stringid; String name; String description; String url; String category; String language; String country; NewsCategoryModel(this.id,this.name,this.description,this.u rl,this.category,this.language,this.country); NewsCategoryModel.fromJson(Map<String,dynamic>parseModel ) { id=parseModel['id']; name=parseModel['name']; description=parseModel['description']; url=parseModel['url']; category=parseModel['category']; language=parseModel['language']; country=parseModel['country']; } static Future callNewsCAtegory() async{ var url="API_KEY"; return get('https://newsapi.org/v2/sources?apiKey=$url&page=1') ; } } 134
  • 135.
    CompleteExample class NewsCategory extendsStatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return NewsCategorystate(); } } class NewsCategorystate extends State<NewsCategory>{ static const _appPrimaryValue = Colors.pink; List<NewsCategoryModel>listNews; List<NewsCategoryModel>listNewsAll; Icon _searchIcon = new Icon(Icons.search); Widget _appBarTitle = new Text( 'Search by Category' ); TextEditingController searchController=new TextEditingController(); List<Color>liscolors=new List(); @override void initState() { // TODO: implement initState super.initState(); liscolors.add(Color(0xFF009688)); liscolors.add(Color(0xFFFF0080)); liscolors.add(Color(0xFF800080)); listNews=new List(); listNewsAll=new List(); //_searchPressed(); searchController.addListener((){ print(searchController.text); if (searchController.text.isEmpty) { setState(() { 135
  • 136.
    listNews.clear(); listNews.addAll(listNewsAll); }); } else { setState((){ listNews.clear(); for(int k=0;k<listNewsAll.length;k++) { print(listNewsAll[k].name+" names "); if(listNewsAll[k].name.toLowerCase().contains(searchControl ler.text.toLowerCase())) { print(listNewsAll[k].name+" names inside"); listNews.add(listNewsAll[k]); } } }); } }); fetchNews(); } @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar( backgroundColor: _appPrimaryValue, title: _appBarTitle, leading: IconButton(icon: this._searchIcon, onPressed: (){ _searchPressed(); }), ), body:(listNews.length>0)?ListView.builder( itemCount: listNews.length, itemBuilder: (ctx,index){ return setNewsItem(listNews[index],index); 136
  • 137.
    }):Center(child: CircularProgressIndicator(),), ); } setNewsItem(NewsCategoryModel newsModel,index) { Colorcolor=Colors.green[700]; if(index==0||index==5||index==11) color=liscolors[0]; else if(index%2==0) color=liscolors[1]; else color=liscolors[2]; return Card( elevation: 5, child: Container( margin: EdgeInsets.all(5), padding: EdgeInsets.all(5), child: InkWell( onTap: (){ HashMap<String,String>hm=new HashMap(); hm['category']=newsModel.category; Navigator.pushNamed(context, "/news",arguments: hm); }, child: Row( children: <Widget>[ Container( height: 50, width: 50, decoration: ShapeDecoration(shape: CircleBorder(),color: color), child: Center(child: Text(newsModel.name.substring(0,1).toUpperCase(),style: TextStyle(fontSize: 30,color: Colors.white),)), ), SizedBox(width:15,), 137
  • 138.
    Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children:<Widget>[ Text(newsModel.name,style: TextStyle(fontSize: 18,color: Colors.black),), SizedBox(height:5,), Text(newsModel.description,style: TextStyle(fontSize: 12,color: Colors.grey[800]),maxLines: 3,), ], ), ) ],), ),), ); } fetchNews(){ var callNews = NewsCategoryModel.callNewsCAtegory(); callNews.then((data){ var response=json.decode(data.body ); print(response); var listNewsdata=response['sources']as List; setState(() { listNews=listNewsdata.map<NewsCategoryModel>((model)=>Ne wsCategoryModel.fromJson(model)).toList(); listNewsAll.clear(); listNewsAll.addAll(listNews); }); },onError: (error){ print("Result Error $error"); } ); 138
  • 139.
    } void _searchPressed() { setState((){ if (this._searchIcon.icon == Icons.search) { this._searchIcon = new Icon(Icons.close); this._appBarTitle = new TextField( controller: searchController, cursorColor: Colors.white, style: TextStyle(color: Colors.white), decoration: new InputDecoration( hintStyle: TextStyle(color: Colors.white), prefixIcon: new Icon(Icons.search,color: Colors.white,), hintText: 'Search...', ), ); } else { this._searchIcon = new Icon(Icons.search); this._appBarTitle = new Text('Search Example'); searchController.clear(); } }); } } 139
  • 140.
  • 141.
    Database and localstorage In Android we have different ways to store data ● SharedPreferences ● Local database ● Files Similarly in Flutter also we can handle the data by above ways SharedPreferences Shared Preferences allow us to save and retrieve data in the form of key,value pair How to access Shared Preferences in Flutter? In Flutter, we can access this feature by using the plugin Shared_Preferences Example class MySharedPref extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return MySharedPrefState(); } } class MySharedPrefState extends State<MySharedPref>{ @override Widget build(BuildContext context) { 141
  • 142.
    // TODO: implementbuild return Scaffold( appBar: AppBar(title: Text("SharedPreference"),backgroundColor: Colors.pink,), floatingActionButton: FloatingActionButton( backgroundColor: Colors.pink, child: Icon(Icons.add), onPressed: (){ _incrementCounter(); }), body: Center( child: FutureBuilder( future: _getIncrementCounter(), builder:(BuildContext context, AsyncSnapshot snapshot) { return Column( mainAxisSize: MainAxisSize.min, children: <Widget>[ Text("The Incremented Value is ",style: TextStyle(color: Colors.pink,fontSize: 20),), SizedBox(height: 20,), Text((snapshot.data==null)?"":snapshot.data.toString(),style: TextStyle(color: Colors.pink,fontSize: 20),), ], ); }, ), ), ); } _incrementCounter() async { SharedPreferences prefs = await SharedPreferences.getInstance(); int counter = (prefs.getInt('counter') ?? 0) + 1; setState(() { prefs.setInt('counter', counter); 142
  • 143.
    }); } Future<int>_getIncrementCounter() async { SharedPreferencesprefs = await SharedPreferences.getInstance(); int counter = (prefs.getInt('counter') ?? 0) + 1; return counter; } } 143
  • 144.
  • 145.
    Database In Android, wecan use SQLite to store structured data that is queried via SQL. How to access SQLite in Flutter? In Flutter, we can use the SQFlite plugin to access this feature of SQFlite SQFlite Features ● Supports transactions and batch operations ● Automatic version management during program opening ● Addition, deletion, change, and help program ● Perform DB operations in iOS and Android background threads SQFlite does not perform a type check on the value, that is, a column of type INTEGER can store TEXT, but when we parse the result of the query and map it, it will report a type exception. So still avoid storing inconsistent data SQFlite supports 5 data types: NULL , INTEGER , REAL , TEXT , BLOB NULL When a column does not store data, the default value is NULL. INTEGER Int type in dart, the value range is -2 ^ 63 to 2 ^ 63-1 REAL 145
  • 146.
    num type indart, namely int and double types TEXT String type in dart BLOB The Uint8List type in dart, although it can store List <int>, is not officially recommended because the conversion is slow bool Stores INTEGER type, 0 is false, 1 is true If we need to store other types of data, such as bool, DateTime, List <String>, etc., we need to handle it by ourselves. Everyone may have their own unique method. I hope you can make some suggestions. We can encapsulate entity classes and parsing classes. From the perspective of external code, these types of storage, such as bool, DateTime, and List <String>, are implemented DateTime Store INTEGER type, the creation time and update time of a column of data is generally more important. Of course, there are other information, such as the payment time, delivery time, and cancellation time of an order. If the TEXT type is stored, the program is inconvenient if it supports multiple languages. List To store TEXT type, we can combine the data into String and store it in the database according to the special separator. It is then parsed into a List <String> according to the split of the String. There are still many things to note, such as the elements of List must not contain the defined delimiters. It is troublesome to modify a certain Item of List, and it can only cover List as a whole. 146
  • 147.
    Map, json, entityclasses Store the TEXT type. Generally, I use the toMap method of the entity class to convert the entity class into a Map. The entity class is converted to a String through jsonEncode. In turn, the string is converted to a Map using jsonDecode, and the entity class is converted from the Map to the entity class Database operations Database creation Open the database based on the name and version number createDB(VERSION) async { String databasesPath = await getDatabasesPath(); // Database Path: /data/user/0/com.package.name/databases String path = join(databasesPath, 'db_name.db'); // Path: /data/user/0/com.package.name/databases/db_name.db Database database = await openDatabase( path, version: VERSION, onCreate: (Database db, int version) async { }, onUpgrade: (Database db, int oldVersion, int newVersion) async { }, ); } Create Table The Create Table query should be inside onCreate() method "CREATE TABLE users(id INTEGER PRIMARY KEY autoincrement, name TEXT, email TEXT, password TEXT, mobile TEXT)"; 147
  • 148.
    Delete Table db.execute('DROP tableusers'); Clear Table db.execute('DELETE FROM users'); Rename Table db.execute('ALTER TABLE users RENAME TO users_1'); Add Field db.execute('ALTER TABLE users ADD gender TEXT'); Delete Field db.execute('ALTER TABLE users DROP COLUMN gender'); Modify Field Type db.execute('ALTER TABLE users ALTER COLUMN value integer'); Insert Insert will returns the last inserted record id int id = await database.rawInsert('INSERT INTO user(name, email, password, mobile) VALUES("shiva", "test@tes.com", "1234#","1234567899")'); Delete returns the number of records affected int count = await database.rawDelete('DELETE FROM user WHERE email = ?', ['test@test.com']); 148
  • 149.
    Update int count =await database.update( 'user', {'name': 'Name 2'} where: 'email = ?', whereArgs: ['test@test.com'] Query Query is the most complicated one in SQL statements. The keywords include distinct, where, group by, having, count, order by asc / desc, limit, offset, in, join, as, union and so on List<Map<String, dynamic>> result = await database.query( 'user', distinct: true, columns: ['name','mobile'], where: 'email = ?', whereArgs: ['test@test.com'], groupBy: 'name', limit: 5, offset: 2, ); List<Map<String, dynamic>> result = await database.rawQuery( 'SELECT * FROM user WHERE email=test@test.com order by name asc limit 5 offset 2', [16], ); Example Add Plugins Add below plugins to pubspec.yaml file sqflite: path: path_provider: ^0.4.1 Create User Model class class User{ int id; String name; String email; 149
  • 150.
    String pasword; String mobile; User(this.name,this.email,this.pasword,this.mobile); Map<String,dynamic>toUserMap(){ return{ 'name':name, 'email':email, 'password':pasword, 'mobile':mobile, }; } static fromMap(Map<String, dynamic> c) { return User(c['name'],c['email'],c['passowrd'],c['mobile']); } } Create Database import 'dart:io'; import 'package:flutter_firebase_app/models/user.dart'; import 'package:path/path.dart'; import 'package:sqflite/sqflite.dart'; import 'package:path_provider/path_provider.dart'; class UserDatabase{ static String path; static final _databaseName = "mydb.db"; static final _databaseVersion = 1; static final _table_user = 'users'; static final _table_logins = 'logins'; UserDatabase._privateConstructor(); static final UserDatabase instance = UserDatabase._privateConstructor(); // only have a single app-wide reference to the database static Database _database; Future<Database> get database async { if (_database != null) return _database; // lazily instantiate the db the first time it is accessed _database = await _initDatabase(); return _database; } // this opens the database (and creates it if it doesn't exist) _initDatabase() async { Directory documentsDirectory = await getApplicationDocumentsDirectory(); String path = join(documentsDirectory.path, _databaseName); 150
  • 151.
    return await openDatabase(path, version:_databaseVersion, onCreate: _onCreate); } // SQL code to create the database table Future _onCreate(Database db, int version) async { await db.execute( "CREATE TABLE users(id INTEGER PRIMARY KEY autoincrement, name TEXT, email TEXT, password TEXT, mobile TEXT)", ); await db.execute( "CREATE TABLE logins(name TEXT, email TEXT, mobile TEXT,password TEXT)", ); } static Future<String> getFileData() async { return getDatabasesPath().then((s){ return path=s; }); } Future<int> insertUser(User user) async{ Database db = await instance.database; var users=await db.rawQuery("select * from users where mobile = "+user.mobile); if(users.length>0) { return -1; } return await db.insert("users",user.toUserMap(),conflictAlgorithm: ConflictAlgorithm.ignore ); } Future<User> checkUserLogin(String mobile, String password) async { Database db = await instance.database; var res=await db.rawQuery("select * from users where mobile = '$mobile' and password = '$password'"); if(res.length>0) { List<dynamic> list = res.toList().map((c) => User.fromMap(c)).toList() ; print("Data "+list.toString()); await db.insert("logins",list[0].toUserMap()); return list[0]; } return null; } Future<int> getUser() async{ Database db = await instance.database; var logins=await db.rawQuery("select * from logins"); if(logins==null) 151
  • 152.
    return 0; return logins.length; } Future<User>getUserData() async{ Database db = await instance.database; var res=await db.rawQuery("select * from logins"); print("result user data $res"); print("result user data "+res.toString()); List<dynamic> list = res.toList().map((c) => User.fromMap(c)).toList() ; return list[0]; } Future<int> deleteUser(String mobile) async{ Database db = await instance.database; var logins= db.delete(_table_logins, where: "mobile = ?", whereArgs: [mobile]); return logins; } } Signup Page import 'package:flutter/material.dart'; import 'package:flutter_firebase_app/databases/UserDatabase.dart'; import 'package:flutter_firebase_app/models/user.dart'; class SignupPage extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return SignUpState(); } } class SignUpState extends State<SignupPage>{ final _formKey = GlobalKey<FormState>(); final _scafoldKey = GlobalKey<ScaffoldState>(); final _nameEditController=TextEditingController(); final _emailEditController=TextEditingController(); final _mobileEditController=TextEditingController(); final _passwordEditController=TextEditingController(); String email_pattern = r'^(([^<>()[].,;:s@"]+(.[^<>()[].,;:s@"]+)*)|(".+"))@(([[0-9]{1, 3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}])|(([a-zA-Z-0-9]+.)+[a-zA-Z]{2,}))$' ; String password_pattern = r'^[a-zA-Z0-9]{6,}$'; String mobile_pattern = r'^(?([0-9]{3}))?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$'; Size size; 152
  • 153.
    @override Widget build(BuildContext context){ size=MediaQuery.of(context).size; return new Scaffold( key: _scafoldKey, body: Stack( children:<Widget>[ Image.asset("splash_img.png",fit: BoxFit.cover, width: size.width,height: size.height,), Container(color: const Color(0x99FFFFFF),), Container( height: 120, decoration: new BoxDecoration( border: Border.all(color: Colors.teal), borderRadius: BorderRadius.only(bottomLeft: Radius.circular(size.width/2),topRight: Radius.circular(size.width/2)), color: Colors.teal, ), ), Center( child: SingleChildScrollView( child: Padding( padding: EdgeInsets.only(left: 20,right: 20), child: Form( key: _formKey, child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ SizedBox(height: 20,), Container( decoration: new BoxDecoration( border: new Border.all(color: Colors.teal), borderRadius: BorderRadius.circular(10), color: Colors.teal, ), child: Padding( padding: const EdgeInsets.all(8.0), child: Text("Registration Form",style: TextStyle(color: Colors.white, fontSize: 22 ),), ), ), SizedBox(height: 40,), //--------------Name FormFiled------------------------------------------ TextFormField( controller: _nameEditController, textInputAction: TextInputAction.next, validator: (value){ if(value.isEmpty) { return "Enter Name"; 153
  • 154.
    } return null; }, style: getTextStyle(), decoration:customInputDecoration("Enter Name"), ), SizedBox(height: 20,), //--------------Email FormFiled------------------------------------------ TextFormField( controller: _emailEditController, textInputAction: TextInputAction.next, validator: (value){ RegExp regex =RegExp(email_pattern); if (!regex.hasMatch(value)) return 'Enter Valid Email'; else return null; }, keyboardType: TextInputType.emailAddress, style: getTextStyle(), decoration: customInputDecoration("Enter email id"), ), SizedBox(height: 20,), //--------------Mobile FormFiled------------------------------------------ TextFormField( controller: _mobileEditController, textInputAction: TextInputAction.next, validator: (value){ RegExp regex =RegExp(mobile_pattern); if (!regex.hasMatch(value)) return 'Enter valid mobile number'; else return null; return null; }, keyboardType: TextInputType.number, maxLength: 10, style: getTextStyle(), decoration: customInputDecoration("Enter mobile number"), ), SizedBox(height: 20,), //--------------Password FormFiled------------------------------------------ TextFormField( controller: _passwordEditController, textInputAction: TextInputAction.done, validator: (value){ RegExp regex =RegExp(password_pattern); if (!regex.hasMatch(value)) return 'Password should be in alphanumaric with 6 characters'; else return null; }, obscureText: true, style: getTextStyle(), decoration: customInputDecoration("Enter password"), 154
  • 155.
    ), SizedBox(height: 20,), RaisedButton(onPressed: (){ if(_formKey.currentState.validate()) { UserDatabase.instance.insertUser(User(_nameEditController.text,_emailEditContr oller.text,_passwordEditController.text,_mobileEditController.text)).then((res ult){ if(result==-1) { _scafoldKey.currentState .showSnackBar(SnackBar(content:Text('User with same number already existed $result'))); }else { _scafoldKey.currentState .showSnackBar(SnackBar(content: Text('User Registered Succesfully $result'))); Navigator.pushReplacementNamed(context, "/login"); } }) ; } }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(18), ), color: Colors.pink, child: Text("Signup", style: TextStyle(color: Colors.white,fontSize: 20),), ), FlatButton( child: Text("Already have account, Sign In?"), onPressed: (){ Navigator.pushReplacementNamed(context, "/login"); }, ) ], ) ), ), ), ) ], ), );; } TextStyle getTextStyle(){ return TextStyle( fontSize: 18, color: Colors.pink 155
  • 156.
    ); } InputDecoration customInputDecoration(String hint) { returnInputDecoration( hintText: hint, hintStyle: TextStyle( color: Colors.teal ), contentPadding: EdgeInsets.all(10), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: Colors.pink ) ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: Colors.pink ) ), ); } } Login Page import 'package:flutter/material.dart'; import 'package:flutter_firebase_app/databases/UserDatabase.dart'; class LoginPage extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return LoginState(); } } class LoginState extends State<LoginPage> { final _formKey = GlobalKey<FormState>(); final _scaffoldKey = GlobalKey<ScaffoldState>(); final _mobileController=TextEditingController(); final _passwordController=TextEditingController(); final FocusNode _mobileFocus = FocusNode(); final FocusNode _passwordFocus = FocusNode(); Size size; @override Widget build(BuildContext context) { size = MediaQuery.of(context).size; 156
  • 157.
    return new Scaffold( key:_scaffoldKey, body: Stack( children:<Widget>[ Image.asset("splash_img.png",fit: BoxFit.cover, width: size.width, height: size.height, ), Padding( padding: EdgeInsets.only(left: 20,right: 20), child: Form( key: _formKey, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ SizedBox(height: 20,), TextFormField( controller: _mobileController, keyboardType: TextInputType.number, textInputAction: TextInputAction.next, focusNode: _mobileFocus, onFieldSubmitted: (term){ FocusScope.of(context).requestFocus(_passwordFocus); }, validator: (value){ if(value.isEmpty) { return "Enter mobile number"; } return null; }, style: getTextStyle(), decoration: customInputDecoration("Enter mobile number"), ), SizedBox(height: 20,), TextFormField( textInputAction: TextInputAction.done, controller: _passwordController, keyboardType: TextInputType.text, obscureText: true, focusNode: _passwordFocus, validator: (value){ if(value.isEmpty) { return "Enter Password"; } return null; }, style: getTextStyle(), decoration: customInputDecoration("Enter password"), ), SizedBox(height: 20,), RaisedButton(onPressed: (){ if(_formKey.currentState.validate()) { 157
  • 158.
    UserDatabase.instance.checkUserLogin(_mobileController.text,_passwordControlle r.text).then((result){ if(result==null) { _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text("Please enter validdetails"))); } else { Navigator.pushReplacementNamed(context, "/home"); } }); } }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(18), ), color: Colors.pink, child: Text("Login", style: TextStyle(color: Colors.white,fontSize: 20),), ), FlatButton( child: Text("Don't have account, Signup?"), onPressed: (){ Navigator.pushReplacementNamed(context, "/signup"); }, ) ], ) ), ) ] , ), ); } TextStyle getTextStyle(){ return TextStyle( fontSize: 18, color: Colors.pink ); } InputDecoration customInputDecoration(String hint) { return InputDecoration( hintText: hint, hintStyle: TextStyle( color: Colors.teal ), contentPadding: EdgeInsets.all(10), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: Colors.pink ) 158
  • 159.
    ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide:BorderSide( color: Colors.pink ) ), ); } } Home Page import 'package:flutter/material.dart'; import 'package:flutter_firebase_app/databases/UserDatabase.dart'; import 'package:flutter_firebase_app/models/user.dart'; class Homepage extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return HomeState(); } } class HomeState extends State<Homepage>{ Size size; User user; @override void initState() { // TODO: implement initState super.initState(); UserDatabase.instance.getUserData().then((result){ setState(() { user=result; }); }); } @override Widget build(BuildContext context) { size=MediaQuery.of(context).size; return Scaffold( appBar: AppBar( title: Text("Home"), ), body: Column( mainAxisSize: MainAxisSize.max, children: <Widget>[ Row( mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[ 159
  • 160.
    Padding( padding: const EdgeInsets.all(12.0), child:RaisedButton( onPressed: (){ UserDatabase.instance.deleteUser(user.mobile).then((res){ if(res==1) { Navigator.pushReplacementNamed(context, "/login"); } }); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15), ), color:Colors.pink, child: Text("Logout", style: TextStyle(color: Colors.white ),) ), ) ], ), Container( height:size.height-200 , child: Center( child: (user==null)?null:Text("Welcome User "+user.name), ), ), ], ) ); } } Check sample at http://rrtutors.com/description/8 160
  • 161.
    State Management We arealways looking for a powerful way of state management. We knows flutter itself has provided us with state management Stateful widget Flutter has different ways of estate management topics Stateful Widget InheritedWidget Provider BLoC Stateful Widget Widgets which will change its behaviour/state dynamically called stateful widgets Stateful widgets are useful when the part of the user interface you are describing can change dynamically. User interfaces need to respond to a variety of things: The user doing something in the user interface. Receiving data from another computer. This is what Stateful Widgets are for. They store data (state) in an associated State class and they can respond when that data (state) changes as the result of the user doing something Let's create a Counter app with Stateful widget class MyStatefulwidget extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return MyState(); 161
  • 162.
    } } class MyState extendsState<MyStatefulwidget>{ int count; @override void initState() { // TODO: implement initState super.initState(); count=0; } @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text("Statefulwidget"),backgroundColor: Colors.pink,), floatingActionButton: FloatingActionButton(onPressed: (){ setState(() { count++; }); },child: Icon(Icons.add,color: Colors.white,),backgroundColor: Colors.pink,), body: Container( child: Center( child: Text("Count : $count",style: TextStyle(color: Colors.pink,fontSize: 20),), ), ), ); } } here on tap on add button every time we are updating the state by calling the setState() method, so this will entirely rebuild the widget This will be useful when a single widget needs to update the State. InheritedWidget Flutter provides an InheritedWidget that can define provide context to every widget below it in the tree. InheritedWidget is a special widget that can store and retrieve data , and subcomponents can obtain stored data. Commonly used MediaQuery and Theme are inherited from InheritedWidget 162
  • 163.
    While this isnice in theory, we can see that it takes quite a lot of code to get a basic example wired up. Fortunately, there are libraries like Bloc, Redux, and Scoped Model abstract this complexity away Let's check the counter app with InheritedWidget Create InheritedWidget class CounterInherited extends InheritedWidget{ final Map _counter={'count':0}; Widget child; CounterInherited ({@required Widget this. child}):super(child:child); get counter=>_counter['count']; increment() { _counter['count']++; } @override bool updateShouldNotify(InheritedWidget oldWidget) { // TODO: implement updateShouldNotify return true; } static CounterInherited of(BuildContext ctx)=> ctx.inheritFromWidgetOfExactType(CounterInherited); } Here we are extending the class with InheritedWidget, which have override method will tell the widget to need to update state or not @override bool updateShouldNotify(InheritedWidget oldWidget) { // TODO: implement updateShouldNotify return true; } Create Child widget Child widget will have the counter increment UI 163
  • 164.
    class Counter extendsStatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return CounterState(); } } class CounterState extends State<Counter>{ @override Widget build(BuildContext context) { int count=CounterInherited.of(context).counter; return Scaffold( appBar: AppBar(title: Text("InheritedWidget"),centerTitle: true,backgroundColor: Colors.pink,), floatingActionButton: FloatingActionButton(onPressed: (){ setState((){}); CounterInherited.of(context).increment(); },child: Icon(Icons.add,color: Colors.white,),backgroundColor: Colors.pink,), body: Container( child: Center( child: Text("Count : $count",style: TextStyle(color: Colors.pink,fontSize: 20),), ), ), ); } } This Child widget need parent widget instance to update counter value, This will done by static method of CounterInherited widget CounterInherited.of(context) Main Widget Now create a widget which contains parent widget as Inherited widget and pass child widget of our counter widget class MyInherited extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return CounterInherited( child: Counter(), ); } 164
  • 165.
    } void main() =>runApp(MyInherited ()); BLoC Flutter, however, brings a new reactive style that is not entirely compatible with MVC. Its design idea is to separate data from views, and render views by data mapping A variation of this classical pattern has emerged from the Flutter community – BLoC What is BLoC? BLoC stands for Business Logic Components, BLoC is a method of building applications using reactive programming. This is a completely asynchronous world composed of streams ● Wrap stateful components with StreamBuilder, streambuilder will listen for a stream ● This stream comes from BLoC ● The data in the stateful widget comes from the listening stream. ● User interaction gestures are detected and events are generated. For example, press the button. ● Call the function of bloc to handle this event ● After processing in bloc, the latest data will be added to the sink of the stream. ● StreamBuilder listens to new data, generates a new snapshot, and calls the build method again ● Widget is rebuilt 165
  • 166.
    Example Here we codinga simple counter application with BLoC This Example show the Number counts in the first page, in the second page we are increase the counter number, this will reflect in the fistpage For this we are going to create app with below steps Create bloc model CountBLoC class CountBLoC{ int _count = 0; var _countController = StreamController<int>.broadcast(); Stream<int> get stream => _countController.stream; int get value => _count; addCount() { _countController.sink.add(++_count); } dispose() { _countController.close(); } } Create Provider class BlocProvider extends InheritedWidget{ CountBLoC bLoC = CountBLoC(); BlocProvider({Key key, Widget child}) : super(key: key, child: child); @override bool updateShouldNotify(InheritedWidget oldWidget) { // TODO: implement updateShouldNotify return true; } static CountBLoC of(BuildContext context) => (context.inheritFromWidgetOfExactType(BlocProvider) as BlocProvider).bLoC; } 166
  • 167.
    Create First Page ThisPage will show the number counts, for this we are accessing the data with streambuilder class CountPage extends StatelessWidget{ @override Widget build(BuildContext context) { final bloc = BlocProvider.of(context); return Scaffold( backgroundColor: Colors.white, appBar: AppBar( title: Text('Counter Page',), backgroundColor: Colors.pink, ), body: Center( child: StreamBuilder<int>( stream: bloc.stream, initialData: bloc.value, builder: (BuildContext context, AsyncSnapshot<int> snapshot) { return Text( 'Number Counts : ${snapshot.data}',style: TextStyle(color: Colors.pink,fontSize: 20), ); }), ), floatingActionButton: FloatingActionButton( backgroundColor: Colors.pink, child: Icon(Icons.add,color: Colors.white,), onPressed: () => Navigator.of(context).push(MaterialPageRoute(builder: (context) => DisplayPage()))), ); } } Create Second page This Page will increase the count by tap on button. With calling the addCount function will increase the count value class DisplayPage extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build final bloc = BlocProvider.of(context); print('build'); return Scaffold( backgroundColor: Colors.white, appBar: AppBar( 167
  • 168.
    title: Text('Update Count'), backgroundColor:Colors.pink, ), body: Center( child: StreamBuilder( stream: bloc.stream, initialData: bloc.value, builder: (context, snapshot) => Center( child: Text( " Counter : ${snapshot.data} nIncreate Count by Button tap", style: TextStyle(color: Colors.pink,fontSize: 20),textAlign: TextAlign.center,), )), ), floatingActionButton: FloatingActionButton( onPressed: () => bloc.addCount(), backgroundColor: Colors.pink, child: Icon(Icons.add,color: Colors.white,), ), ); } } Now It’s time to check out Main Page Here we are using the Provider to load the child widgets to Inherited widget. class MyBloc extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( child: MaterialApp( title: 'BLoC', theme: ThemeData.dark(), home: CountPage(), ), ); } } 168
  • 169.
    Firebase Integration Firebase isa mobile and web application development platform developed by Firebase. Firebase supports frameworks like Flutter on a best-effort basis. Now we are going to learn how to setup firebase for Flutter Application Firebase Setup Step 1: Create a new Project in on firebase console (https://console.firebase.google.com/) This will ask to login with gmail. Login with your account. Now create new project in Firebase console After creating project it will navigates to Console dashboard 169
  • 170.
    Now select Android,now it will display below screen 170
  • 171.
    Add your applicationpackage name and register application Download google-service.json file and this under android->app folder 171
  • 172.
    Modify your build.gradlefiles to use the plugin project level build.gradle file buildscript { repositories { // Check that you have the following line (if not, add it): google() // Google's Maven repository } dependencies { // ... // Add the following line: classpath 'com.google.gms:google-services:4.3.3' // Google Services plugin } } allprojects { // ... repositories { // Check that you have the following line (if not, add it): google() // Google's Maven repository // ... } } app level build.gradle file apply plugin: 'com.google.gms.google-services' That's it for Android setup iOS Setup Copy & Paste your Downloaded GoogleService-Info.plist into projectname/ios/Runner folder Open projectname/ios/PODFile (Execute pod install if file not found) & Add ; pod 'Firebase/Core; and execute pod install 172
  • 173.
    Open projectname/ios/Runner.xcworkspace &Choose Project & Add File to Runner & Choose GoogleService-Info.plist with choose target of Runner 173
  • 174.
    That's it nowour application is ready for use Firebase Firebase authentication & Google sign in using Flutter Firebase Authentication provides backend services, easy-to-use SDKs, and ready-made UI libraries to authenticate users to your app. It supports authentication using passwords, phone numbers, popular federated identity providers like Google, Facebook and Twitter, and more Now we are going to learn how to set up and implementing Google Signin using Firebase authentication Step 1: Create Flutter application Step 2: Add dependencies Add required dependencies in pubspec.yaml file 174
  • 175.
    dependencies: firebase_auth: ^0.15.4 google_sign_in: ^4.1.4 andrun flutter packages get in terminal Step 3: import required packages in to the main file import 'package:firebase_auth/firebase_auth.dart'; import 'package:google_sign_in/google_sign_in.dart'; Step 4: Design Login Screen import 'package:flutter/material.dart'; class LoginPage extends StatefulWidget { @override _LoginPageState createState() => _LoginPageState(); } class _LoginPageState extends State<LoginPage> { @override Widget build(BuildContext context) { return Scaffold( body: Container( color: Colors.white, child: Center( child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ FlutterLogo(size: 150), SizedBox(height: 50), _signInButton(), ], ), ), ), ); } Widget _signInButton() { return OutlineButton( 175
  • 176.
    splashColor: Colors.grey, onPressed: (){}, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(40)), highlightElevation: 0, borderSide: BorderSide(color: Colors.grey), child: Padding( padding: const EdgeInsets.fromLTRB(0, 10, 0, 10), child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Image(image: AssetImage("assets/google_logo.jpg"), height: 35.0), Padding( padding: const EdgeInsets.only(left: 10), child: Text( 'Sign in with Google', style: TextStyle( fontSize: 20, color: Colors.pink, ), ), ) ], ), ), ); } } Step 5: Authentication Now, we will need to create an instance of FirebaseAuth & GoogleSignIn final FirebaseAuth _auth = FirebaseAuth.instance; final GoogleSignIn googleSignIn = GoogleSignIn(); Future<String> signInWithGoogle() async { final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn(); final GoogleSignInAuthentication googleSignInAuthentication = await googleSignInAccount.authentication; final AuthCredential credential = GoogleAuthProvider.getCredential( accessToken: googleSignInAuthentication.accessToken, idToken: googleSignInAuthentication.idToken, ); final AuthResult authResult = await _auth.signInWithCredential(credential); final FirebaseUser user = authResult.user; assert(!user.isAnonymous); assert(await user.getIdToken() != null); final FirebaseUser currentUser = await _auth.currentUser(); assert(user.uid == currentUser.uid); 176
  • 177.
    return 'signInWithGoogle succeeded:$user'; } void signOutGoogle() async{ await googleSignIn.signOut(); print("User Sign Out"); } Now update login button onPressed functionality onPressed: () { signInWithGoogle().whenComplete(() { Navigator.of(context).push( MaterialPageRoute( builder: (context) { return MyHomePage(); }, ), ); }); } Step 6: Check User Sign In We can check already user Signin or not by below code void isSignedIn() async { isLoggedIn = await googleSignIn.isSignedIn(); if (isLoggedIn) { Navigator.push( context, MaterialPageRoute(builder: (context) => MyHomePage()), ); } } 177
  • 178.
    Complete code import 'package:firebase_auth/firebase_auth.dart'; import'package:flutter/material.dart'; import 'package:google_sign_in/google_sign_in.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'homepage.dart'; class LoginPage extends StatefulWidget { @override _LoginPageState createState() => _LoginPageState(); } class _LoginPageState extends State<LoginPage> { final FirebaseAuth _auth = FirebaseAuth.instance; 178
  • 179.
    final GoogleSignIn googleSignIn= GoogleSignIn(); bool isLoggedIn=false; @override void initState() { // TODO: implement initState super.initState(); isSignedIn(); } @override Widget build(BuildContext context) { return Scaffold( body: Container( color: Colors.white, child: Center( child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ FlutterLogo(size: 150), SizedBox(height: 50), _signInButton(), ], ), ), ), ); } Widget _signInButton() { return OutlineButton( splashColor: Colors.grey, onPressed: () { signInWithGoogle().whenComplete(() { Navigator.of(context).pushReplacement( MaterialPageRoute( builder: (context) { return MyHomePage(); }, ), ); }); }, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(40)), highlightElevation: 0, borderSide: BorderSide(color: Colors.grey), child: Padding( padding: const EdgeInsets.fromLTRB(0, 10, 0, 10), child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Image(image: AssetImage("assets/google_logo.jpg"), height: 35.0,width: 40,), Padding( padding: const EdgeInsets.only(left: 10), child: Text( 'Sign in with Google', style: TextStyle( 179
  • 180.
    fontSize: 20, color: Colors.pink, ), ), ) ], ), ), ); } Future<String>signInWithGoogle() async { final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn(); final GoogleSignInAuthentication googleSignInAuthentication = await googleSignInAccount.authentication; final AuthCredential credential = GoogleAuthProvider.getCredential( accessToken: googleSignInAuthentication.accessToken, idToken: googleSignInAuthentication.idToken, ); final AuthResult authResult = await _auth.signInWithCredential(credential); final FirebaseUser user = authResult.user; assert(!user.isAnonymous); assert(await user.getIdToken() != null); final FirebaseUser currentUser = await _auth.currentUser(); assert(user.uid == currentUser.uid); return 'signInWithGoogle succeeded: $user'; } void signOutGoogle() async{ await googleSignIn.signOut(); print("User Sign Out"); } void isSignedIn() async { isLoggedIn = await googleSignIn.isSignedIn(); if (isLoggedIn) { Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => MyHomePage()), ); } } } 180
  • 181.
    Chat Application withFirebase database Flutter Now we are going to learn how to create Chat app with Firebase Database Step 1: Create flutter application Step 2: Create Users list page Homepage.dart In this Page we are fetching all the users data from Firebase database and display import 'dart:async'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_database/firebase_database.dart'; import 'package:flutter/material.dart'; import 'package:flutter_firbase/user.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'chatpage.dart'; class MyHomePage extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return MyHomePageState(); } } class MyHomePageState extends State<MyHomePage> { FirebaseDatabase fireDB=FirebaseDatabase.instance; TextEditingController _txtCtrl = TextEditingController(); FirebaseUser currentUser; String id; String nickname=""; String photoUrl; List<User>listUsers=new List(); @override void initState() { // TODO: implement initState super.initState(); getUserInfo(); } getUserInfo() async { 181
  • 182.
    SharedPreferences prefs =await SharedPreferences.getInstance(); setState(() { id=prefs.getString("id"); nickname=prefs.getString("nickname"); photoUrl=prefs.getString("photoUrl"); }); FirebaseDatabase.instance.reference().child('users/-M1ZPGcD23x7uAUew5fF') .orderByChild("created_at") .onValue .listen((event) { print("data user"); Map<dynamic,dynamic>hm=event.snapshot.value; if(hm!=null) { hm.forEach((key, value) { print(hm[key]); }); } }); print("Fetching datad 0 "); StreamSubscription<Event> subscription =fireDB .reference() .child("users") .onValue .listen((Event event) { print("Fetching datad 1 "); print( event.snapshot.value); Map<dynamic,dynamic>hm=event.snapshot.value; hm.forEach((key, value) { Map<dynamic,dynamic>users=value; setState(() { if(users['id']!=id) listUsers.add(new User(users['name'],users['id'],users['photoUrl'])); }); }); },onError: (errror){ print("Error ${errror.toString()}"); }); } @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text("Chat $nickname"),), body: Column( mainAxisAlignment: MainAxisAlignment.start, children: <Widget>[ Expanded( child:(listUsers.length>0)? ListView.separated( separatorBuilder: (context, index) { return Divider( color: Colors.black, 182
  • 183.
    ); }, itemCount: listUsers.length, itemBuilder: (context,index){ returnListTile( title: Text(listUsers[index].name), leading: Material(child: Image.network(listUsers[index].img,),borderRadius: BorderRadius.all( Radius.circular(32.0), ), clipBehavior: Clip.hardEdge,), onTap: (){ Navigator.push(context, MaterialPageRoute(builder: (context){ return ChatPage(myid: id,uid:listUsers[index].id ,myImg: photoUrl,uImg: listUsers[index].img,myName: nickname,uName:listUsers[index].name ,); })); }, ); }):Center(child: CircularProgressIndicator(),) ), Container( margin: EdgeInsets.all(8), ) ]), ); } String peerId="1"; String peerAvatar; var listMessage; String groupChatId="1"; SharedPreferences prefs; } Step 3: Create Chat page chatpage.dart In this page we are sending the Messages to selected user and display list of messages between two users import 'dart:async'; import 'dart:convert'; import 'package:firebase_database/firebase_database.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'message.dart'; class ChatPage extends StatefulWidget{ 183
  • 184.
    const ChatPage({@required this.myid,@requiredthis.uid,@required this.myName,@required this.uName,@required this.myImg,@required this.uImg}); final String myid; final String uid; final String myName; final String uName; final String myImg; final String uImg; @override State<StatefulWidget> createState() { // TODO: implement createState return ChatPageState(myid,uid,myName,uName,myImg,uImg); } } class ChatPageState extends State{ final String myid; final String uid; final String myName; final String uName; final String myImg; final String uImg; TextEditingController _txtCtrl=new TextEditingController(); FirebaseDatabase fireDB=FirebaseDatabase.instance; ChatPageState( this.myid, this.uid,this.myName, this.uName,this.myImg,this.uImg); List<Messages>list; @override void initState() { // TODO: implement initState super.initState(); list=new List(); fetchMessages(); /*FirebaseDatabase().reference() .child("messages") .child(myid+"_"+uid) .limitToLast(1) .orderByChild("created_at") .onChildAdded .listen((event) { Map<dynamic,dynamic>hm=event.snapshot.value; print("Event ${hm.length}"); int k=0; hm.forEach((key, value) { print("Fetching Msgs "); print(key); if(k==hm.length) { setState(() { Map map=hm[key]; list.add(Messages.fromMap(value,key)); }); } k++; }); 184
  • 185.
    });*/ } @override Widget build(BuildContext context){ // TODO: implement build return Scaffold( appBar: AppBar( leading: Material(child: Image.network(uImg,width: 10,height: 10,),borderRadius: BorderRadius.all( Radius.circular(32.0), ), clipBehavior: Clip.hardEdge,), title: Text(uName), backgroundColor: Colors.pink, ), body: Container( child: Column( children: <Widget>[ Expanded( child: (list.length>0)?ListView.separated( separatorBuilder: ( context, index) => Divider(color: Colors.grey,), itemCount: list.length, reverse: false, itemBuilder: (context, index) { return buildItem(index,list[index]); } ,):Center(child: CircularProgressIndicator(), ), ), Container( margin: EdgeInsets.all(8), child: Row(children: <Widget>[ Expanded(child: TextField( controller: _txtCtrl,decoration: InputDecoration( hintText: "Write your Message", border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: Colors.pink) )),)), SizedBox( width: 80, child: FloatingActionButton( onPressed: () => sendMessage(), child: Icon(Icons.send), )) ])) ], ), ), ); } sendMessage() { fireDB .reference() .child("messages") .child(getUniqueId(myid,uid)) .child(DateTime.now().toUtc().millisecondsSinceEpoch.toString()) .push().set({ 185
  • 186.
    "from_id": myid, "to_id": uid, "from_name":myName, "to_name": uName, "msg": _txtCtrl.text }).then((value) => { _txtCtrl.text = '' }); } fetchMessages() { fireDB .reference() .child("messages") .child(getUniqueId(myid,uid)) .onValue .listen((Event event) { print("Fetching datad"); print( event.snapshot.value); Map<dynamic,dynamic>hm=event.snapshot.value; if(hm!=null) { list.clear(); hm.forEach((key, value) { print("Fetching Msgs $value"); print(key); Map m=value; m.forEach((key, value) { setState(() { list.add(Messages.fromMap(value,key)); }); }); }); } }); //StreamSubscription<Event> subscription = } Widget buildItem(int index, Messages msg) { if (msg.from_id == myid) { // Right (my message) return Column( children: [ Container( child: Column( mainAxisSize: MainAxisSize.min, 186
  • 187.
    crossAxisAlignment: CrossAxisAlignment.end, children: <Widget>[ Text( msg.msg, style:TextStyle(color: Colors.black), ), /*SizedBox(height: 10,), (msg.created_at!=null)? Text( "${readTimestamp(msg.created_at )}", style: TextStyle(color: Colors.black,fontSize: 8), ):Text("")*/ ], ), padding: EdgeInsets.fromLTRB(15.0, 10.0, 15.0, 10.0), width: 200.0, decoration: ShapeDecoration(shape: OutlineInputBorder(borderRadius: BorderRadius.circular(8),borderSide: BorderSide(color: Colors.pink))), margin: EdgeInsets.only(bottom: 10.0, right: 10.0), ) ], mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end, ); } else { // Left (peer message) return Container( child: Column( children: <Widget>[ Container( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: <Widget>[ Text( msg.msg, style: TextStyle(color: Colors.black), ), /*SizedBox(height: 10,), (msg.created_at!=null)? Text( ("${readTimestamp(msg.created_at )}"), style: TextStyle(color: Colors.black,fontSize: 8), ):Text("")*/ ], ), padding: EdgeInsets.fromLTRB(15.0, 10.0, 15.0, 10.0), width: 200.0, decoration: ShapeDecoration(shape: OutlineInputBorder(borderRadius: BorderRadius.circular(8))), margin: EdgeInsets.only(left: 10.0), ) ], crossAxisAlignment: CrossAxisAlignment.start, ), margin: EdgeInsets.only(bottom: 10.0), ); } } 187
  • 188.
    static String getUniqueId(Stringi1,String i2){ if(i1.compareTo(i2)<=-1){ return i1+i2; } else{ return i2+i1; } } String readTimestamp(DateTime timestamp) { var now = DateTime.now(); return now.difference(timestamp).inHours.toString(); /* var format = DateFormat('HH:mm a'); var date = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000); var diff = now.difference(date); var time = ''; if (diff.inSeconds <= 0 || diff.inSeconds > 0 && diff.inMinutes == 0 || diff.inMinutes > 0 && diff.inHours == 0 || diff.inHours > 0 && diff.inDays == 0) { time = format.format(date); } else if (diff.inDays > 0 && diff.inDays < 7) { if (diff.inDays == 1) { time = diff.inDays.toString() + ' DAY AGO'; } else { time = diff.inDays.toString() + ' DAYS AGO'; } } else { if (diff.inDays == 7) { time = (diff.inDays / 7).floor().toString() + ' WEEK AGO'; } else { time = (diff.inDays / 7).floor().toString() + ' WEEKS AGO'; } } return time;*/ } } Step 5: Run application 188
  • 189.