├── source ├── background_image.dart ├── indentical_method.dart ├── password_encryption.dart ├── appbar_image_backgound.dart.dart ├── dateformatter.dart ├── nummerical_range_formatter.dart ├── groupBy.dart ├── list_generate.dart ├── testappcastrrl.xml ├── DecimalTextInputFormatter.dart ├── Download_html_and_show_view.dart ├── expansion_panel.dart └── dynamic_expansion_panel.dart └── README.md /source/background_image.dart: -------------------------------------------------------------------------------- 1 | Container( 2 | height: 120.0, 3 | width: 120.0, 4 | decoration: BoxDecoration( 5 | image: DecorationImage( 6 | image: AssetImage( 7 | 'assets/assets/alucard.jpg'), 8 | fit: BoxFit.fill, 9 | ), 10 | shape: BoxShape.circle, 11 | ), 12 | ) 13 | -------------------------------------------------------------------------------- /source/indentical_method.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | void main() { 3 | 4 | 5 | List l1 = [1, 2, 3, 4]; 6 | List l2 = [1, 2, 3, 4]; 7 | print(identical(l1, l2)); // false 8 | 9 | List l1 = [1, 2, 3, 4]; 10 | List l2 = [1, 2, 3, 4]; 11 | print(identical(l1.toString(), l2.toString())); 12 | 13 | List l1 = [1, 2, 3,4]; 14 | List l2 = [1, 2, 3, 4]; 15 | print(listEquals(l1, l2)); //true 16 | } 17 | -------------------------------------------------------------------------------- /source/password_encryption.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart' show rootBundle; 2 | 3 | import 'package:encrypt/encrypt.dart'; 4 | import 'package:pointycastle/asymmetric/api.dart'; 5 | 6 | Future getEncryptedData(String plainText) async { 7 | final publicPem = await rootBundle.loadString('assets/public.pem'); 8 | 9 | final publicKey = RSAKeyParser().parse(publicPem) as RSAPublicKey; 10 | 11 | final encrypt = Encrypter(RSA( 12 | publicKey: publicKey, 13 | )); 14 | 15 | final encrypted = encrypt.encrypt(plainText); 16 | 17 | logger.d(encrypted.base64); 18 | 19 | return encrypted.base64; 20 | } 21 | -------------------------------------------------------------------------------- /source/appbar_image_backgound.dart.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | void main() => runApp(const MyApp()); 4 | 5 | class MyApp extends StatelessWidget { 6 | const MyApp({Key key}) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | var title = 'Web Images'; 11 | 12 | return MaterialApp( 13 | title: title, 14 | home: Scaffold( 15 | appBar: AppBar( 16 | titleSpacing: 0, 17 | title: SizedBox( 18 | width: double.infinity, 19 | child: Image.asset('assets/images/test.jpg'), 20 | ), 21 | backgroundColor: const Color(0xffFA7343), 22 | ), 23 | body: Container( 24 | ), 25 | ), 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /source/dateformatter.dart: -------------------------------------------------------------------------------- 1 | import 'package:intl/intl.dart'; 2 | 3 | void main() { 4 | var data = "14/12/2021 03:34:03 PM"; 5 | 6 | print(getFormattedDateFromFormattedString( 7 | value: data, 8 | currentFormat: "yyyy/MM/dd HH:mm:ss a", 9 | desiredFormat: "hh:mm a")); 10 | } 11 | 12 | String getFormattedDateFromFormattedString( 13 | {required value, 14 | required String currentFormat, 15 | required String desiredFormat, 16 | isUtc = false}) { 17 | String formattedDate = ""; 18 | if (value != null || value.isNotEmpty) { 19 | try { 20 | DateTime dateTime = 21 | DateFormat(currentFormat).parse(value, isUtc).toLocal(); 22 | formattedDate = DateFormat(desiredFormat).format(dateTime); 23 | } catch (e) { 24 | print("$e"); 25 | } 26 | } 27 | // print("Formatted date time: $formattedDate"); 28 | return formattedDate; 29 | } 30 | -------------------------------------------------------------------------------- /source/nummerical_range_formatter.dart: -------------------------------------------------------------------------------- 1 | class NumericalRangeFormatter extends TextInputFormatter { 2 | final double min; 3 | final double max; 4 | 5 | NumericalRangeFormatter({required this.min, required this.max}); 6 | 7 | @override 8 | TextEditingValue formatEditUpdate( 9 | TextEditingValue oldValue, 10 | TextEditingValue newValue, 11 | ) { 12 | if ('.'.allMatches(newValue.text).length <= 1) { 13 | if (newValue.text.replaceAll("\$", "") == '') { 14 | return newValue; 15 | } else if (double.parse(newValue.text.replaceAll("\$", "")) < min) { 16 | return const TextEditingValue().copyWith(text: min.toStringAsFixed(2)); 17 | } else { 18 | return double.parse(newValue.text.replaceAll("\$", "")) > max 19 | ? oldValue 20 | : newValue; 21 | } 22 | } else { 23 | return oldValue; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /source/groupBy.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | List data = [ 3 | Model("A", 'php', "test"), 4 | Model("A", 'flutter', 'engineer1'), 5 | Model("A", 'flutter', 'engineer2'), 6 | Model('B', 'c sharp', 'engineer1'), 7 | Model('B', 'c sharp', 'engineer2'), 8 | Model('B', 'dot net', 'engineer2'), 9 | Model('B', 'php', 'engineer2'), 10 | Model('C', 'php', 'engineer2'), 11 | Model('C', 'php', 'engineer1') 12 | ]; 13 | 14 | Map group = {}; 15 | 16 | final releaseDateMap = data.groupBy((m) => m.team); 17 | 18 | print(releaseDateMap); 19 | } 20 | 21 | extension Iterables on Iterable { 22 | Map> groupBy(K Function(E) keyFunction) => fold( 23 | >{}, 24 | (Map> map, E element) => 25 | map..putIfAbsent(keyFunction(element), () => []).add(element)); 26 | } 27 | 28 | /// model class 29 | class Model { 30 | Model(this.team, this.projectName, this.engineersName); 31 | String team; 32 | String projectName; 33 | String engineersName; 34 | } 35 | -------------------------------------------------------------------------------- /source/list_generate.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | void main() => runApp(const MyApp()); 4 | 5 | class MyApp extends StatelessWidget { 6 | const MyApp({Key key}) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | String title = 'List generate'; 11 | 12 | List src = ["test1", "test2"]; 13 | 14 | return MaterialApp( 15 | title: title, 16 | home: Scaffold( 17 | appBar: AppBar( 18 | titleSpacing: 0, 19 | title: Text(title), 20 | backgroundColor: const Color(0xffFA7343), 21 | ), 22 | body: Container( 23 | width: double.infinity, 24 | height: 500, 25 | // color: Colors.red, 26 | child: ListView( 27 | children: List.generate( 28 | src.length, 29 | (index) { 30 | return Container(child: Text('${src[index]}')); 31 | }, 32 | ), 33 | ), 34 | ), 35 | ), 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /source/testappcastrrl.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Sparkle Test App Changelog 4 | http://sparkle-project.org/files/sparkletestcast.xml 5 | Most recent changes with links to updates. 6 | en 7 | 8 | Version 2.0 9 | https://sparkle-project.org 10 | 2.0 11 | 12 |
  • Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  • Suspendisse sed felis ac ante ultrices rhoncus. Etiam quis elit vel nibh placerat facilisis in id leo.
  • Vestibulum nec tortor odio, nec malesuada libero. Cras vel convallis nunc.
  • Suspendisse tristique massa eget velit consequat tincidunt. Praesent sodales hendrerit pretium.
  • ]]> 13 |
    14 | Sat, 26 Jul 2014 15:20:11 +0000 15 | 16 |
    17 |
    18 |
    19 | -------------------------------------------------------------------------------- /source/DecimalTextInputFormatter.dart: -------------------------------------------------------------------------------- 1 | class DecimalTextInputFormatter extends TextInputFormatter { 2 | final int decimalRange; 3 | final int min; 4 | final int max; 5 | 6 | DecimalTextInputFormatter( 7 | {required this.decimalRange, required this.min, required this.max}); 8 | 9 | @override 10 | TextEditingValue formatEditUpdate( 11 | TextEditingValue oldValue, // unused. 12 | TextEditingValue newValue, 13 | ) { 14 | TextSelection newSelection = newValue.selection; 15 | String truncated = newValue.text; 16 | 17 | if (decimalRange > 0 && '.'.allMatches(newValue.text).length <= 1) { 18 | String value = newValue.text; 19 | 20 | if (value.contains(".") && 21 | value.substring(value.indexOf(".") + 1).length > decimalRange) { 22 | truncated = oldValue.text; 23 | newSelection = oldValue.selection; 24 | } else if (value == ".") { 25 | truncated = "0."; 26 | 27 | newSelection = newValue.selection.copyWith( 28 | baseOffset: math.min(truncated.length, truncated.length + 1), 29 | extentOffset: math.min(truncated.length, truncated.length + 1), 30 | ); 31 | } 32 | return TextEditingValue( 33 | text: truncated, 34 | selection: newSelection, 35 | composing: TextRange.empty, 36 | ); 37 | } else { 38 | return oldValue; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /source/Download_html_and_show_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_html/flutter_html.dart'; 3 | import 'package:http/http.dart' as http; 4 | 5 | void main() { 6 | runApp(const MyApp()); 7 | } 8 | 9 | class MyApp extends StatelessWidget { 10 | const MyApp({Key key}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return const MaterialApp( 15 | title: 'Flutter Demo', 16 | home: MyHomePage(), 17 | ); 18 | } 19 | } 20 | 21 | class MyHomePage extends StatelessWidget { 22 | const MyHomePage({Key key}) : super(key: key); 23 | @override 24 | Widget build(BuildContext context) { 25 | return Scaffold( 26 | body: _buildBody(context), 27 | ); 28 | } 29 | 30 | Widget _buildBody(BuildContext context) { 31 | return SingleChildScrollView( 32 | child: FutureBuilder( 33 | future: fetchHTML('https://google.com'), 34 | builder: (context, snapshoot){ 35 | return snapshoot.hasData?Html( 36 | data: snapshoot.data, 37 | ):Container(); 38 | }, 39 | )); 40 | } 41 | } 42 | 43 | Future fetchHTML(String url1) async { 44 | var url = Uri.parse(url1); 45 | final response = await http.get(url); 46 | 47 | if (response.statusCode == 200) 48 | return response.body; 49 | else throw Exception('Failed'); 50 | } 51 | -------------------------------------------------------------------------------- /source/expansion_panel.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | const Color darkBlue = Color.fromARGB(255, 18, 32, 47); 4 | 5 | void main() { 6 | runApp(MaterialApp(home: MyApp())); 7 | } 8 | 9 | class MyApp extends StatefulWidget { 10 | @override 11 | State createState() => _MyAppState(); 12 | } 13 | 14 | class _MyAppState extends State { 15 | var List1 = { 16 | 'title': true, 17 | 'open': false, 18 | 'title2': true, 19 | 'open2': false, 20 | 'title3': true, 21 | 'open3': false, 22 | 'title4': true, 23 | 'open4': false, 24 | 'title5': true, 25 | 'open5': false, 26 | }; 27 | List chipNames = [ 28 | "time with friend and family hurrrrrrrrrrh 3495872039847 0239487502 0234857 08 02948357092834 ", 29 | "healthy device usage and test", 30 | "Eating healthy", 31 | "Pulse checks", 32 | "Exercise", 33 | "Serving others" 34 | ]; 35 | 36 | List _isOpen = [true, false]; 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | return MaterialApp( 41 | debugShowCheckedModeBanner: false, 42 | theme: ThemeData.dark().copyWith( 43 | colorScheme: ColorScheme.light().copyWith(primary: Colors.tealAccent), 44 | ), 45 | home: SafeArea( 46 | child: Scaffold( 47 | backgroundColor: Theme.of(context).colorScheme.primary, 48 | appBar: AppBar( 49 | title: Text("Title of the App"), 50 | ), 51 | body: Column( 52 | children: [ 53 | Container( 54 | height: 200, 55 | color: Colors.green, 56 | ), 57 | Expanded( 58 | child: SingleChildScrollView( 59 | child: Column( 60 | children: [ 61 | 62 | ExpansionPanelList( 63 | children: [ 64 | ExpansionPanel( 65 | isExpanded: _isOpen[0], 66 | headerBuilder: (context, isOpen) { 67 | return Text("Activities"); 68 | }, 69 | body: Column( 70 | mainAxisSize: MainAxisSize.min, 71 | children: [ 72 | _myListView(), 73 | // _myListView(), 74 | ], 75 | ), 76 | ), 77 | ExpansionPanel( 78 | isExpanded: _isOpen[1], 79 | headerBuilder: (context, isOpen) { 80 | return Text("Ports"); 81 | }, 82 | body: Text("Test!!!"), 83 | ) 84 | ], 85 | expansionCallback: (i, isOpen) { 86 | setState(() { 87 | _isOpen[i] = !isOpen; 88 | }); 89 | }, 90 | ) 91 | ], 92 | ), 93 | ), 94 | ) 95 | ], 96 | )), 97 | ), 98 | ); 99 | } 100 | 101 | SelectableChip({String label}) { 102 | return Text("$label"); 103 | } 104 | Widget _myListView() { 105 | return MediaQuery.removePadding( 106 | context: context, 107 | removeTop: true, 108 | child: ListView( 109 | shrinkWrap: true, 110 | physics: ScrollPhysics(), 111 | children: List1.keys.map((String key) { 112 | return new CheckboxListTile( 113 | title: new Text(key), 114 | value: List1[key], 115 | activeColor: Colors.black, 116 | checkColor: Colors.white, 117 | onChanged: (bool value) { 118 | setState(() { 119 | List1[key] = value; 120 | }); 121 | }, 122 | ); 123 | }).toList(), 124 | ), 125 | ); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /source/dynamic_expansion_panel.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | const Color darkBlue = Color.fromARGB(255, 18, 32, 47); 4 | 5 | void main() { 6 | runApp(MaterialApp(home: MyApp())); 7 | } 8 | 9 | class MyApp extends StatefulWidget { 10 | @override 11 | State createState() => _MyAppState(); 12 | } 13 | 14 | class _MyAppState extends State { 15 | var List1 = { 16 | 'title': true, 17 | 'open': false, 18 | 'title2': true, 19 | 'open2': false, 20 | 'title3': true, 21 | 'open3': false, 22 | 'title4': true, 23 | 'open4': false, 24 | 'title5': true, 25 | 'open5': false, 26 | }; 27 | 28 | List List2 = [ 29 | 'Bubble Football ⚽', 30 | 'Futsal 🧿', 31 | 'Beach Volleyball 🏐', 32 | 'Volleyball 🏐', 33 | 'Dodgeball 🏀', 34 | 'Rugby 🏉', 35 | 'American Footbal 🏈', 36 | 'Korftbal 🥎', 37 | 'Netbal ⚾', 38 | ]; 39 | 40 | List _isOpen = [true, false]; 41 | 42 | List _selected_box = List(); 43 | int selectItem; 44 | 45 | @override 46 | Widget build(BuildContext context) { 47 | return MaterialApp( 48 | debugShowCheckedModeBanner: false, 49 | theme: ThemeData.dark().copyWith( 50 | colorScheme: ColorScheme.light().copyWith(primary: Colors.tealAccent), 51 | ), 52 | home: SafeArea( 53 | child: Scaffold( 54 | backgroundColor: Theme.of(context).colorScheme.primary, 55 | appBar: AppBar( 56 | title: Text("Title of the App"), 57 | ), 58 | body: Column( 59 | children: [ 60 | Expanded( 61 | child: SingleChildScrollView( 62 | child: Column( 63 | children: [ 64 | ExpansionPanelList( 65 | children: List.generate( 66 | List2.length, 67 | (index) => expansionPanel( 68 | _selected_box.contains(index), 69 | "${List2[index]}"), 70 | ), 71 | expansionCallback: (i, isOpen) { 72 | setState(() { 73 | if (_selected_box.contains(i)) 74 | _selected_box.remove(i); 75 | else 76 | _selected_box.add(i); 77 | }); 78 | }, 79 | ) 80 | ], 81 | ), 82 | ), 83 | ) 84 | ], 85 | )), 86 | ), 87 | ); 88 | } 89 | 90 | ExpansionPanel expansionPanel(bool isbool, String expansionTitle) { 91 | return ExpansionPanel( 92 | isExpanded: isbool ?? false, 93 | headerBuilder: (context, isOpen) { 94 | return Row( 95 | children: [ 96 | SizedBox(width: 10), 97 | Center( 98 | child: Text( 99 | "$expansionTitle", 100 | style: TextStyle( 101 | fontSize: 17, 102 | fontWeight: FontWeight.w400, 103 | color: Color(0xff333333)), 104 | ), 105 | ), 106 | ], 107 | ); 108 | }, 109 | body: SingleChildScrollView( 110 | child: Column( 111 | mainAxisSize: MainAxisSize.min, 112 | children: [ 113 | _myListView(), 114 | // _myListView(), 115 | ], 116 | ), 117 | ), 118 | ); 119 | } 120 | 121 | Widget _myListView() { 122 | return MediaQuery.removePadding( 123 | context: context, 124 | removeTop: true, 125 | child: ListView( 126 | shrinkWrap: true, 127 | physics: ScrollPhysics(), 128 | children: List1.keys.map((String key) { 129 | return CheckboxListTile( 130 | title: Text(key), 131 | value: List1[key], 132 | activeColor: Colors.black, 133 | checkColor: Colors.white, 134 | onChanged: (bool value) { 135 | setState(() { 136 | List1[key] = value; 137 | }); 138 | }, 139 | ); 140 | }).toList(), 141 | ), 142 | ); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Flutter_Road_Map_Documentation 2 | 3 | ![Flutter-road-map](https://user-images.githubusercontent.com/26745548/143763624-8b33d260-bd4f-441b-879f-3a3ac20063b2.jpeg) 4 | 5 | ## Content 6 | * [Official Flutter](https://flutter.dev/) 7 | * [Flutter Flow](https://flutterflow.io/) 8 | * [How can you change the default ScrollPhysics in flutter?](https://stackoverflow.com/questions/62809540/how-can-you-change-the-default-scrollphysics-in-flutter) 9 | * [Generate Dynamic Card](https://mirfahim.medium.com/how-to-generate-dynamic-card-in-flutter-with-listview-builder-and-floatingactionbutton-f925ec8e81a5) 10 | * [How to remove scroll glow?](https://stackoverflow.com/questions/51119795/how-to-remove-scroll-glow) 11 | * [GetX](https://pub.dev/packages/get) 12 | * [Provider](https://pub.dev/packages/provider) 13 | * Flutter bloc:
    14 | ** [Documents](https://bloclibrary.dev/#/flutterbloccoreconcepts)
    15 | ** [Source Code](https://github.com/Jahidul007/hacker_news)
    16 | ** [Video](https://www.youtube.com/watch?v=27EP04T824Y&ab_channel=CODEVILLAGE) 17 | * [Shimmer](https://pub.dev/packages/shimmer) 18 | * [Staggered](https://pub.dev/packages/flutter_staggered_grid_view) 19 | * Reactive Programming or RxDart:
    20 | ** [Source Code](https://github.com/Jahidul007/Movie_App)
    21 | ** [Video](https://www.youtube.com/watch?v=x4FKXw4Uvls&list=PL_Wj0DgxTlJc8E3ulwdekyVI4Wc819azh&index=1&ab_channel=ProgrammingAddict) 22 | * [sync*, async*, yield, and yield*](https://jelenaaa.medium.com/what-are-sync-async-yield-and-yield-in-dart-defe57d06381) 23 | * [Flutter keys](https://youtu.be/kn0EOS-ZiIc) 24 | * [Multiscreen app](https://medium.com/flutter-community/clean-navigation-in-flutter-using-generated-routes-891bd6e000df) 25 | * [in flutter page textfield hidden by keyboard](https://stackoverflow.com/questions/54196213/in-flutter-page-textfield-hidden-by-keyboard) 26 | * [Measuring your app's size](https://flutter.dev/docs/perf/app-size) 27 | * [Dropdown search](https://pub.dev/packages/dropdown_search) 28 | * [Flutter push notification step by step](https://www.freecodecamp.org/news/how-to-add-push-notifications-to-flutter-app/) 29 | * [Flutter WEB download option and show in html view](https://github.com/Jahidul007/Flutter_Roadmap/blob/main/source/Download_html_and_show_view.dart) 30 | * [Flutter Hooks](https://pub.dev/packages/flutter_hooks) 31 | * [Flutter GraphQl](https://pub.dev/packages/graphql_flutter) 32 | * [Flutter grpc](https://pub.dev/packages/grpc) 33 | 34 | ## Courses 35 | * [The Complete 2020 Flutter Development Bootcamp with Dart](https://www.udemy.com/course/flutter-bootcamp-with-dart/) 36 | * [Santos Enoque](https://www.youtube.com/channel/UCRl79zOEtiLCglAFZJJzEZQ) 37 | * [Vandad Nahavandipoor - Free Flutter Course](https://www.youtube.com/playlist?list=PL6yRaaP0WPkVtoeNIGqILtRAgd3h2CNpT) 38 | 39 | 40 | 41 | ## Flutter Clean Architechture 42 | 43 | #### Data 44 | The Data layer consists of repository and data models. Repository is where the application gather all data related to the use case, either from local sources (Local DB, cookies) or remote sources (remote API). Data from the sources, usually in json format, is parsed to Dart object called models. It’s need to be done to make sure the application knows what data and variable it’s expecting. 45 | 46 | 47 | #### Domain 48 | Domain is the inner layer which shouldn’t be susceptible to the whims of changing data sources or porting our app to Angular Dart. It will contain only the core business logic (use cases) and business objects (entities). 49 | 50 | Repository classes act as the Data Layer and Domain Layer, each function on the repository class acts as the domain layer that specifies the use cases of the feature. 51 | 52 | #### Presentation 53 | Presentation is where the UI goes. You obviously need widgets to display something on the screen. These widgets is controlled by the state using various state management design pattern used in Flutter. In this project, I use BLoC as the state management. 54 | 55 | BLoC allows us to know exactly what data is given to the state. There is BLoC implementation that uses event classes to easily predict what state is the application in, but I use the simpler implementation of BLoC (just using streams) to shorten times for other that hasn’t been familiar to BLoC before. But I won’t dive too deep on BLoC because it’s another thing to write 56 | 57 | ![FLutter Clean Architechture](https://user-images.githubusercontent.com/56557098/103347921-9e9afb80-4ac2-11eb-8259-769d93bef2fd.jpg) 58 | 59 | ### Code to generate the folder pattern 60 | ```dart 61 | import 'dart:convert'; 62 | import 'dart:io'; 63 | 64 | void main(List arguments) async { 65 | Map> directoryList = { 66 | 'Data': [ 67 | 'RepositoryModels', 68 | 'Repositories/LocalRepository', 69 | 'Repositories/RemoteRepository', 70 | ], 71 | 'Domain': [ 72 | 'Entities', 73 | ], 74 | 'Presentation': [ 75 | 'Controllers', 76 | 'Utilities', 77 | 'Widgets', 78 | 'Functions', 79 | 'Language', 80 | 'Pages', 81 | ], 82 | }; 83 | //Add your the list of pages in your app/website 84 | 85 | List pageList = [ 86 | /*Sample List*/ 87 | "OnBoardings", 88 | "Sign In", 89 | "Sign Up", 90 | "Home", 91 | "Item List", 92 | "Settings", 93 | "Help", 94 | "Chat" 95 | ]; 96 | 97 | ///Creating the directories 98 | await directoryList.forEach((key, value) async { 99 | await directoryList[key].forEach((element) async { 100 | await makeFile("$key/$element/export" + 101 | "${element.replaceAll(' ', '_').toLowerCase()}.dart"); 102 | }); 103 | }); 104 | 105 | ///Creating the Functions and Widgets directories in every pages 106 | await pageList.forEach((element) async { 107 | ///Create The Page Folder 108 | await makeFile("Presentation/Pages/" + 109 | "${element.toLowerCase()}/${element.replaceAll(' ', '_').toLowerCase()}Page.dart"); 110 | 111 | ///Create The Page's Functions Folder 112 | await makeFile( 113 | "Presentation/Pages/${element.toLowerCase()}/Functions/${element.replaceAll(' ', '_').toLowerCase()}Functions.dart"); 114 | 115 | ///Create The Page's Widgets Folder 116 | await makeFile( 117 | "Presentation/Pages/${element.toLowerCase()}/Widgets/${element.replaceAll(' ', '_').toLowerCase()}Widgets.dart"); 118 | 119 | ///Add The page in the export file 120 | await File("Presentation/Pages/exportpages.dart").writeAsStringSync( 121 | "export '${element.toLowerCase()}/${element.replaceAll(' ', '_').toLowerCase()}Page.dart';\n", 122 | mode: FileMode.append, 123 | ); 124 | }); 125 | 126 | // print("Export Pages:\n" + exportPages); 127 | } 128 | 129 | makeDir(String s) async { 130 | var dir = await Directory(s).create(recursive: true); 131 | // print(dir.path); 132 | } 133 | 134 | makeFile(String s) async { 135 | var dir = await File(s).create(recursive: true); 136 | // print(dir.path); 137 | } 138 | 139 | 140 | ``` 141 | 142 | ## Feature Driven Project Architecture 143 | ├── lib 144 | | ├── posts 145 | │ │ ├── bloc 146 | │ │ │ └── post_bloc.dart 147 | | | | └── post_event.dart 148 | | | | └── post_state.dart 149 | | | └── models 150 | | | | └── models.dart* 151 | | | | └── post.dart 152 | │ │ └── view 153 | │ │ | ├── posts_page.dart 154 | │ │ | └── posts_list.dart 155 | | | | └── view.dart* 156 | | | └── widgets 157 | | | | └── bottom_loader.dart 158 | | | | └── post_list_item.dart 159 | | | | └── widgets.dart* 160 | │ │ ├── posts.dart* 161 | │ ├── app.dart 162 | │ ├── simple_bloc_observer.dart 163 | │ └── main.dart 164 | ├── pubspec.lock 165 | ├── pubspec.yaml 166 | The application uses a feature-driven directory structure. This project structure enables us to scale the project by having self-contained features. In this example we will only have a single feature (the post feature) and it's split up into respective folders with barrel files, indicated by the asterisk (*). 167 | 168 | ## Reactive Programming with MVVM pattern 169 | It basically consists of three layers where the View which consists of all UI elements you see on the screen. It can also contain logic that only concerns the UI. The Model contains your business logic and backend connections. The ViewModel is the glue between both in that it processes and transforms data from the Model to a form the View can easily display. It offers functions (often called Commands) the View can call to trigger actions on the Model and provides events to signal the View about data changes. 170 | 171 | Important: The ViewModel knows nothing about the View which makes it testable and more than one View can use the same ViewModel. A ViewModel just offers services to the View (events, commands). The _View decides which it uses. 172 | 173 | **"Several MVVM frameworks encapsulate the subscription of events and calling functions to update data in the ViewModel from the View using DataBindings"** 174 | 175 | To trigger any other action in the View besides data updates from the ViewModel gets tedious because you have to publish events and functions if the View should be able to retrieve data as a result of the event. 176 | 177 | Another problem is that we always moving state between the different layers which have to be kept in sync. 178 | 179 | What we really want is an App that just reacts on any event from the outside without having to deal with state management all the time. 180 | 181 | ![MVVM](https://user-images.githubusercontent.com/26745548/103972805-fa871380-5197-11eb-9275-1ce108c3e499.png) 182 | 183 | ## code Snippet 184 | * Dismissable item from listview onClick 185 | 186 | ```dart 187 | onClick:(){ 188 | removeItem(index); 189 | } 190 | 191 | void removeItem(index) { 192 | setState(() { 193 | groupData.removeAt(index); 194 | }); 195 | } 196 | ``` 197 | N.B: Must be followed stateful widget. 198 | 199 | * Reorderable List 200 | ```dart 201 | void onReorder(int oldIndex, int newIndex) { 202 | if (newIndex > oldIndex) { 203 | newIndex -= 1; 204 | } 205 | setState(() { 206 | String game = topTenGames[oldIndex]; 207 | 208 | topTenGames.removeAt(oldIndex); 209 | topTenGames.insert(newIndex, game); 210 | }); 211 | } 212 | ``` 213 | 214 | * iso8601 date/time convert to standard format 215 | ```dart 216 | var date = "2021-03-24T08:57:47.812" 217 | var dateTime = DateTime.parse("${date.substring(0,16)}"); 218 | var stdTime = DateFormat('MMM d, yy hh:mm a').format(dateTime).toString(); 219 | ``` 220 | 221 | * convert tiem to local 222 | ``` 223 | var dateFormat = DateFormat("dd-MM-yyyy hh:mm aa"); // you can change the format here 224 | var utcDate = dateFormat.format(DateTime.parse(uTCTime)); // pass the UTC time here 225 | var localDate = dateFormat.parse(utcDate, true).toLocal().toString(); 226 | String createdDate = dateFormat.format(DateTime.parse(localDate)); 227 | ``` 228 | 229 | * List to map of map 230 | ``` 231 | List _options = [ 232 | 'Arts & entertainment', 233 | 'Biographies & memoirs', 234 | ]; 235 | 236 | List _isOptionSelected = [ 237 | false, 238 | false, 239 | ]; 240 | 241 | 242 | 243 | Map map = _options.asMap().map((key,value)=>MapEntry(value,{ 244 | 'optionName':value, 245 | 'isOptionSelected':_isOptionSelected[key] 246 | })); 247 | ``` 248 | * map of map to list 249 | ``` 250 | 251 | List _options = []; 252 | List _isOptionSelected = []; 253 | Map> _isOptionMap = { 254 | 'Arts & entertainment': { 255 | 'optionName': 'Arts & entertainment', 256 | 'isOptionSelected': false, 257 | }, 258 | 'Biographies & memoirs': { 259 | 'optionName': 'Biographies & memoirs', 260 | 'isOptionSelected': false, 261 | }, 262 | }; 263 | _isOptionMap.forEach((key, value) { 264 | _options.add(value['optionName']); 265 | _isOptionSelected.add(value['isOptionSelected']); 266 | 267 | }); 268 | print(_options); 269 | print(_isOptionSelected); 270 | ``` 271 | * How can I limit the size of a text field in flutter? 272 | ``` 273 | TextEditingController _controller = new TextEditingController(); 274 | String text = ""; // empty string to carry what was there before it 275 | onChanged 276 | int maxLength = ... 277 | ... 278 | new TextField( 279 | controller: _controller, 280 | onChange: (String newVal) { 281 | if(newVal.length <= maxLength){ 282 | text = newVal; 283 | }else{ 284 | _controller.value = new TextEditingValue( 285 | text: text, 286 | selection: new TextSelection( 287 | baseOffset: maxLength, 288 | extentOffset: maxLength, 289 | affinity: TextAffinity.downstream, 290 | isDirectional: false 291 | ), 292 | composing: new TextRange( 293 | start: 0, end: maxLength 294 | ) 295 | ); 296 | _controller.text = text; 297 | } 298 | } 299 | ); 300 | ``` 301 | 302 | * Date picker with time 303 | ``` 304 | DateTimePicker( 305 | type: DateTimePickerType.dateTimeSeparate, 306 | dateMask: 'yyyy-MM-dd', 307 | initialValue: DateTime.now().toString(), 308 | firstDate: DateTime(2000), 309 | lastDate: DateTime(2100), 310 | icon: Icon(Icons.event), 311 | dateLabelText: 'Date', 312 | timeLabelText: "Hour", 313 | 314 | onChanged: (val) { 315 | var date = val.substring(0,10); 316 | var time = val.substring(11); 317 | reportBloc.updateFromDate(date+"T"+time+":00.000Z"); 318 | }, 319 | validator: (val) { 320 | return null; 321 | }, 322 | onSaved: (val) { 323 | reportBloc.updateFromDate(val); 324 | }, 325 | ), 326 | ``` 327 | * initialize alertBox when app open 328 | ``` 329 | WidgetsBinding.instance.addPostFrameCallback((_) async { 330 | await showDialog( 331 | context: context, 332 | builder: (BuildContext context) => AnimatedPadding( 333 | padding: MediaQuery.of(context).viewInsets, 334 | duration: const Duration(milliseconds: 100), 335 | curve: Curves.decelerate, 336 | child: LoadingWrap( 337 | padding: EdgeInsets.all(20), 338 | children: [ 339 | new AlertDialog( 340 | insetPadding: EdgeInsets.all(20), 341 | contentPadding: EdgeInsets.all(0), 342 | content: Container( 343 | height: 200, 344 | width: MediaQuery.of(context).size.width, 345 | child: SingleChildScrollView( 346 | child: ListBody( 347 | children: [ 348 | ------------- 349 | ], 350 | ), 351 | ), 352 | ], 353 | ), 354 | ), 355 | ), 356 | actions: [], 357 | ) 358 | ], 359 | ), 360 | ), 361 | ); 362 | }); 363 | ``` 364 | 365 | * Tooltip onTap rather than onLongPress possible? 366 | ``` 367 | GestureDetector( 368 | onTap: () { 369 | final dynamic _toolTip = _toolTipKey.currentState; 370 | _toolTip.ensureTooltipVisible(); 371 | }, 372 | child: Tooltip( 373 | key: _toolTipKey, 374 | waitDuration: Duration(seconds: 1), 375 | showDuration: Duration(seconds: 2), 376 | padding: EdgeInsets.all(5), 377 | height: 16, 378 | message: widget.message??"testing tooltip", 379 | child: SvgPicture.asset("images/ic_info.svg"),), 380 | ), 381 | ],), 382 | ) 383 | 384 | ``` 385 | * Get Customize phone number format 386 | ``` 387 | //flutter_masked_text: ^0.8.0 388 | import 'package:flutter_masked_text/flutter_masked_text.dart'; 389 | 390 | String getPhoneNumber(String phoneNum) { 391 | var controller = new MaskedTextController(text: '', mask: '000-0000'); 392 | controller.updateText(phoneNum); 393 | return controller.text.length >=1 ?"1-868-"+controller.text:"Not Available"; 394 | } 395 | ``` 396 | * swap list value 397 | ``` 398 | balanceTransferDescription 399 | .where((element) => element.name.toLowerCase().contains("other")) 400 | .forEach((element) { 401 | balanceTransferDescription.remove(element); 402 | balanceTransferDescription.insert( 403 | balanceTransferDescription.length, element); 404 | ``` 405 | * Capitalize first letter of the first word for each sentence 406 | ``` 407 | String capitalizeSentence(String s) { 408 | // Each sentence becomes an array element 409 | var sentences = s.split('.'); 410 | // Initialize string as empty string 411 | var output = ''; 412 | // Loop through each sentence 413 | for (var sen in sentences) { 414 | // Trim leading and trailing whitespace 415 | var trimmed = sen.trim(); 416 | // Capitalize first letter of current sentence 417 | var capitalized = "${trimmed[0].toUpperCase() + trimmed.substring(1)}"; 418 | // Add current sentence to output with a period 419 | output += capitalized + ". "; 420 | } 421 | return output; 422 | } 423 | ``` 424 | * Dart remove all brackets with integers between them 425 | ``` 426 | String s = "Hello, world![1] i am 'foo' [100]"; 427 | print(s.replaceAll(new RegExp(r'\s*\[\d+]'),'')); 428 | ``` 429 | * Remove duplicate value from map 430 | ``` 431 | import 'dart:collection'; 432 | 433 | void main() { 434 | List users = [ 435 | {"userEmail": "tintu@gmail.com"}, 436 | {"userEmail": "john@gmail.com"}, 437 | {"userEmail": "tintu@gmail.com"}, 438 | {"userEmail": "rose@gmail.com"}, 439 | {"userEmail": "john@gmail.com"}, 440 | ]; 441 | 442 | var finalList = [ 443 | ...LinkedHashSet( 444 | equals: (user1, user2) => user1['userEmail'] == user2['userEmail'], 445 | hashCode: (user) => user['userEmail'].hashCode, 446 | )..addAll(users) 447 | ]; 448 | print(finalList); 449 | } 450 | ``` 451 | 452 | * Call secondary color from theme data 453 | ``` 454 | // define theme 455 | theme: ThemeData( 456 | primaryColor: Colors.black, 457 | colorScheme: ColorScheme.fromSwatch().copyWith( 458 | secondary: Colors.green, 459 | ), 460 | ), 461 | // call color 462 | color: Theme.of(context).colorScheme.secondary 463 | ``` 464 | 465 | * Indentical methond in double, string, list 466 | 467 | You got the false because a list is an indexable collection of objects with a length. In identical checks, two instances are the same or not but you can convert this instance into a string. 468 | 469 | ``` 470 | List l1 = [1, 2, 3, 4]; 471 | List l2 = [1, 2, 3, 4]; 472 | print(identical(l1.toString(), l2.toString())); //true 473 | ``` 474 | For list comparison, you can use `listEquals` 475 | 476 | ``` 477 | import 'package:flutter/foundation.dart'; 478 | void main() { 479 | List l1 = [1, 2, 3,4]; 480 | List l2 = [1, 2, 3, 4]; 481 | print(listEquals(l1, l2)); //true 482 | } 483 | ``` 484 | 485 | ## Creating a Flutter/Dart package 486 | To create a Flutter package, run the below command: 487 | ``` 488 | flutter create --template=package flutter_pkg 489 | ``` 490 | * The create subcommand is used to create a Flutter project or package. In this case, it will create a Flutter package 491 | * The --template=package flag tells it to create a Flutter package 492 | * The flutter_pkg is the folder in which the Flutter package will be created. You can name it anything you want 493 | 494 | 495 | ## Issue and Error Handling 496 | * [How to solve “No implementation found for method showToast” in Flutter?](https://stackoverflow.com/questions/62286575/how-to-solve-no-implementation-found-for-method-showtoast-in-flutter) 497 | * [Flutter Multiple Blocs and NamedRoutes](https://stackoverflow.com/questions/61060354/flutter-multiple-blocs-and-namedroutes) 498 | * [StreamBuilder vs FlutterBuilder](https://stackoverflow.com/questions/50844519/flutter-streambuilder-vs-futurebuilder) 499 | * [`vsync` property in TabController constructor](https://stackoverflow.com/questions/46851245/vsync-property-in-tabcontroller-constructor) 500 | * [Difference Between Deactivate and Dispose?](https://stackoverflow.com/questions/56387243/difference-between-deactivate-and-dispose) 501 | * [Populating stepper content based](https://stackoverflow.com/questions/59130685/flutter-populating-steps-content-based-on-previous-selection-on-stepper) 502 | * [How to build dependent dropdown menus in Flutter?](https://stackoverflow.com/questions/60323767/multiple-dependent-dropdown-in-flutter) 503 | * [Flutter building apk: Duplicate class found in modules guava-26.0-android.jar and listenablefuture-1.0.jar](https://stackoverflow.com/questions/63711300/flutter-building-apk-duplicate-class-found-in-modules-guava-26-0-android-jar-an) 504 | * [Exception: Concurrent modification during iteration: Instance(length:17) of '_GrowableList'](https://stackoverflow.com/questions/22409666/exception-concurrent-modification-during-iteration-instancelength17-of-gr) 505 | * [regular-expressions with number](https://www.oreilly.com/library/view/regular-expressions-cookbook/9781449327453/ch06s07.html) 506 | * [The purpose of function `runZoned` of 'dart:async'](https://stackoverflow.com/questions/21531924/the-purpose-of-function-runzoned-of-dartasync) 507 | * Unable to import module app_setings or missing your manifest.lock in Xcode - Flutter 508 | > Try the following steps in terminal: 509 | > flutter pub get 510 | > pod init 511 | > pod install 512 | 513 | 514 | 515 | --------------------------------------------------------------------------------