Skip to content

This is a flutter roadmap and documentation repository. If anyone is interested you can join the party to help the community and make flutter great again.

Notifications You must be signed in to change notification settings

Jahidul007/Flutter_Roadmap

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 

Repository files navigation

Flutter_Road_Map_Documentation

Flutter-road-map

Content

Courses

Flutter Clean Architechture

Data

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.

Domain

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).

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.

Presentation

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.

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

FLutter Clean Architechture

Code to generate the folder pattern

import 'dart:convert';
import 'dart:io';

void main(List<String> arguments) async {
  Map<String, List<String>> directoryList = {
    'Data': [
      'RepositoryModels',
      'Repositories/LocalRepository',
      'Repositories/RemoteRepository',
    ],
    'Domain': [
      'Entities',
    ],
    'Presentation': [
      'Controllers',
      'Utilities',
      'Widgets',
      'Functions',
      'Language',
      'Pages',
    ],
  };
  //Add your the list of pages in your app/website

  List<String> pageList = [
    /*Sample List*/
    "OnBoardings",
    "Sign In",
    "Sign Up",
    "Home",
    "Item List",
    "Settings",
    "Help",
    "Chat"
  ];

  ///Creating the directories
  await directoryList.forEach((key, value) async {
    await directoryList[key].forEach((element) async {
      await makeFile("$key/$element/export" +
          "${element.replaceAll(' ', '_').toLowerCase()}.dart");
    });
  });

  ///Creating the Functions and Widgets directories in every pages
  await pageList.forEach((element) async {
    ///Create The Page Folder
    await makeFile("Presentation/Pages/" +
        "${element.toLowerCase()}/${element.replaceAll(' ', '_').toLowerCase()}Page.dart");

    ///Create The Page's Functions Folder
    await makeFile(
        "Presentation/Pages/${element.toLowerCase()}/Functions/${element.replaceAll(' ', '_').toLowerCase()}Functions.dart");

    ///Create The Page's Widgets Folder
    await makeFile(
        "Presentation/Pages/${element.toLowerCase()}/Widgets/${element.replaceAll(' ', '_').toLowerCase()}Widgets.dart");

    ///Add The page in the export file
    await File("Presentation/Pages/exportpages.dart").writeAsStringSync(
      "export '${element.toLowerCase()}/${element.replaceAll(' ', '_').toLowerCase()}Page.dart';\n",
      mode: FileMode.append,
    );
  });

  // print("Export Pages:\n" + exportPages);
}

makeDir(String s) async {
  var dir = await Directory(s).create(recursive: true);
  // print(dir.path);
}

makeFile(String s) async {
  var dir = await File(s).create(recursive: true);
  // print(dir.path);
}

Feature Driven Project Architecture

  ├── lib
  |   ├── posts
  │   │   ├── bloc
  │   │   │   └── post_bloc.dart
  |   |   |   └── post_event.dart
  |   |   |   └── post_state.dart
  |   |   └── models
  |   |   |   └── models.dart*
  |   |   |   └── post.dart
  │   │   └── view
  │   │   |   ├── posts_page.dart
  │   │   |   └── posts_list.dart
  |   |   |   └── view.dart*
  |   |   └── widgets
  |   |   |   └── bottom_loader.dart
  |   |   |   └── post_list_item.dart
  |   |   |   └── widgets.dart*
  │   │   ├── posts.dart*
  │   ├── app.dart
  │   ├── simple_bloc_observer.dart
  │   └── main.dart
  ├── pubspec.lock
  ├── pubspec.yaml

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 (*).

Reactive Programming with MVVM pattern

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.

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.

"Several MVVM frameworks encapsulate the subscription of events and calling functions to update data in the ViewModel from the View using DataBindings"

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.

Another problem is that we always moving state between the different layers which have to be kept in sync.

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.

MVVM

code Snippet

  • Dismissable item from listview onClick
onClick:(){
  removeItem(index);
}

 void removeItem(index) {
    setState(() {
      groupData.removeAt(index);
    });
  }

N.B: Must be followed stateful widget.

  • Reorderable List
 void onReorder(int oldIndex, int newIndex) {
    if (newIndex > oldIndex) {
      newIndex -= 1;
    }
    setState(() {
      String game = topTenGames[oldIndex];

      topTenGames.removeAt(oldIndex);
      topTenGames.insert(newIndex, game);
    });
  }
  • iso8601 date/time convert to standard format
 var date = "2021-03-24T08:57:47.812" 
 var dateTime =  DateTime.parse("${date.substring(0,16)}");
 var stdTime =  DateFormat('MMM d, yy hh:mm a').format(dateTime).toString();
  • convert tiem to local
var dateFormat = DateFormat("dd-MM-yyyy hh:mm aa"); // you can change the format here
var utcDate = dateFormat.format(DateTime.parse(uTCTime)); // pass the UTC time here
var localDate = dateFormat.parse(utcDate, true).toLocal().toString();
String createdDate = dateFormat.format(DateTime.parse(localDate)); 
  • List to map of map
List<String> _options = [
 'Arts & entertainment',
 'Biographies & memoirs',
];

List<bool> _isOptionSelected = [
 false,
 false,
];



Map<String, dynamic> map =  _options.asMap().map((key,value)=>MapEntry(value,{
 'optionName':value,
 'isOptionSelected':_isOptionSelected[key]
}));
  • map of map to list

List<String> _options = [];
 List<bool> _isOptionSelected = [];
 Map<String, Map<String, dynamic>> _isOptionMap = {
   'Arts & entertainment': {
     'optionName': 'Arts & entertainment',
     'isOptionSelected': false,
   },
   'Biographies & memoirs': {
     'optionName': 'Biographies & memoirs',
     'isOptionSelected': false,
   },
 };
 _isOptionMap.forEach((key, value) {
     _options.add(value['optionName']);
     _isOptionSelected.add(value['isOptionSelected']);
   
 });
 print(_options);
 print(_isOptionSelected);
  • How can I limit the size of a text field in flutter?
TextEditingController _controller = new TextEditingController();
String text = ""; // empty string to carry what was there before it 
onChanged
int maxLength = ...
...
new TextField(
 controller: _controller,
 onChange: (String newVal) {
     if(newVal.length <= maxLength){
         text = newVal;
     }else{
         _controller.value = new TextEditingValue(
             text: text,
             selection: new TextSelection(
                 baseOffset: maxLength,
                 extentOffset: maxLength,
                 affinity: TextAffinity.downstream,
                 isDirectional: false
             ),
             composing: new TextRange(
                 start: 0, end: maxLength
             )
         );
         _controller.text = text;
     } 
 }
);
  • Date picker with time
DateTimePicker(
                   type: DateTimePickerType.dateTimeSeparate,
                   dateMask: 'yyyy-MM-dd',
                   initialValue: DateTime.now().toString(),
                   firstDate: DateTime(2000),
                   lastDate: DateTime(2100),
                   icon: Icon(Icons.event),
                   dateLabelText: 'Date',
                   timeLabelText: "Hour",

                   onChanged: (val) {
                     var date = val.substring(0,10);
                     var time = val.substring(11);
                     reportBloc.updateFromDate(date+"T"+time+":00.000Z");
                   },
                   validator: (val) {
                     return null;
                   },
                   onSaved: (val) {
                     reportBloc.updateFromDate(val);
                   },
                 ),
  • initialize alertBox when app open
WidgetsBinding.instance.addPostFrameCallback((_) async {
   await showDialog(
     context: context,
     builder: (BuildContext context) => AnimatedPadding(
       padding: MediaQuery.of(context).viewInsets,
       duration: const Duration(milliseconds: 100),
       curve: Curves.decelerate,
       child: LoadingWrap(
         padding: EdgeInsets.all(20),
         children: [
           new AlertDialog(
             insetPadding: EdgeInsets.all(20),
             contentPadding: EdgeInsets.all(0),
             content: Container(
               height: 200,
               width: MediaQuery.of(context).size.width,
               child: SingleChildScrollView(
                 child: ListBody(
                   children: <Widget>[
                    -------------
                         ],
                       ),
                     ),
                   ],
                 ),
               ),
             ),
             actions: <Widget>[],
           )
         ],
       ),
     ),
   );
 });
  • Tooltip onTap rather than onLongPress possible?
GestureDetector(
         onTap: () {
           final dynamic _toolTip = _toolTipKey.currentState;
           _toolTip.ensureTooltipVisible();
         },
         child: Tooltip(
           key: _toolTipKey,
           waitDuration: Duration(seconds: 1),
           showDuration: Duration(seconds: 2),
           padding: EdgeInsets.all(5),
           height: 16,
           message: widget.message??"testing tooltip",
           child: SvgPicture.asset("images/ic_info.svg"),),
       ),
     ],),
   )
   
  • Get Customize phone number format
//flutter_masked_text: ^0.8.0
import 'package:flutter_masked_text/flutter_masked_text.dart';

String getPhoneNumber(String phoneNum)  {
 var controller = new MaskedTextController(text: '', mask: '000-0000');
 controller.updateText(phoneNum);
 return controller.text.length >=1 ?"1-868-"+controller.text:"Not Available";
}
  • swap list value
balanceTransferDescription
         .where((element) => element.name.toLowerCase().contains("other"))
         .forEach((element) {
       balanceTransferDescription.remove(element);
       balanceTransferDescription.insert(
           balanceTransferDescription.length, element);
  • Capitalize first letter of the first word for each sentence
String capitalizeSentence(String s) {
  // Each sentence becomes an array element
  var sentences = s.split('.');
  // Initialize string as empty string
  var output = '';
  // Loop through each sentence
  for (var sen in sentences) {
    // Trim leading and trailing whitespace
    var trimmed = sen.trim();
    // Capitalize first letter of current sentence
    var capitalized = "${trimmed[0].toUpperCase() + trimmed.substring(1)}";
    // Add current sentence to output with a period
    output += capitalized + ". ";
  }
  return output;
}
  • Dart remove all brackets with integers between them
String s = "Hello, world![1] i am 'foo' [100]";
print(s.replaceAll(new RegExp(r'\s*\[\d+]'),''));
  • Remove duplicate value from map
import 'dart:collection';

void main() {
  List users = [
    {"userEmail": "[email protected]"},
    {"userEmail": "[email protected]"},
    {"userEmail": "[email protected]"},
    {"userEmail": "[email protected]"},
    {"userEmail": "[email protected]"},
  ];

  var finalList = [
    ...LinkedHashSet(
      equals: (user1, user2) => user1['userEmail'] == user2['userEmail'],
      hashCode: (user) => user['userEmail'].hashCode,
    )..addAll(users)
  ];
  print(finalList);
}
  • Call secondary color from theme data
// define theme
theme: ThemeData(
          primaryColor: Colors.black,
          colorScheme: ColorScheme.fromSwatch().copyWith(
            secondary: Colors.green,
          ),
        ),
 // call color 
 color: Theme.of(context).colorScheme.secondary
  • Indentical methond in double, string, list

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.

  List l1 = [1, 2, 3, 4];
  List l2 = [1, 2, 3, 4];
  print(identical(l1.toString(), l2.toString())); //true

For list comparison, you can use listEquals

import 'package:flutter/foundation.dart';
void main() {
  List<int> l1 = [1, 2, 3,4];
  List<int> l2 = [1, 2, 3, 4];
  print(listEquals(l1, l2)); //true
}

Creating a Flutter/Dart package

To create a Flutter package, run the below command:

flutter create --template=package flutter_pkg
  • The create subcommand is used to create a Flutter project or package. In this case, it will create a Flutter package
  • The --template=package flag tells it to create a Flutter package
  • The flutter_pkg is the folder in which the Flutter package will be created. You can name it anything you want

Issue and Error Handling

Releases

No releases published

Packages

No packages published

Languages