Flutter Widgets🚀

February 18, 2021

Flutter and Dart Widget Sheet

Init

1flutter create my_project
2
## Specify organization name ```bash flutter create --org com.myorg my_project ```

Healthcheck

1flutter doctor
2

Hello World

1import 'package:flutter/material.dart';
2
3void main() {
4    runApp(MyApp());
5}
6
7class MyApp extends StatelessWidget {
8  
9  Widget build(BuildContext context) {
10    return MaterialApp(
11      title: 'Hello world!',
12      home: Scaffold(
13        body: Center(
14          child: Text('Hello world'),
15        ),
16      ),
17    );
18  }
19}
20

Stateless Widget

1import 'package:flutter/material.dart';
2
3class Greeter extends StatelessWidget {
4  Greeter({Key key  this.name}) : super(key: key);
5
6  final String name;
7
8  
9  Widget build(BuildContext context) {
10    return Container(
11      child: Text('Hello, $name'),
12    );
13  }
14}
15

Required and default props

1import 'package:flutter/material.dart';
2
3class SomeComponent extends StatelessWidget {
4  SomeComponent({
5     this.foo,
6    this.bar = 'some string',
7  });
8
9  final String foo;
10  final String bar;
11
12  
13  Widget build(BuildContext context) {
14    return Container(
15      child: Text('$foo $bar'),
16    );
17  }
18}
19

Stateful Widget

1import 'package:flutter/material.dart';
2
3class WidgetWithState extends StatefulWidget {
4  
5  _WidgetWithStateState createState() => _WidgetWithStateState();
6}
7
8class _WidgetWithStateState extends State<WidgetWithState> {
9  int counter = 0;
10
11  increment() {
12    setState(() {
13      counter++;
14    });
15  }
16
17  decrement() {
18    setState(() {
19      counter--;
20    });
21  }
22
23  
24  Widget build(BuildContext context) {
25    return Row(
26      children: <Widget>[
27        FlatButton(onPressed: increment, child: Text('Increment')),
28        FlatButton(onPressed: decrement, child: Text('Decrement')),
29        Text(counter.toString()),
30
31    );
32  }
33}
34

Combining props and state

1import 'package:flutter/material.dart';
2
3class SomeWidget extends StatefulWidget {
4  SomeWidget({ this.fruit});
5
6  final String fruit;
7
8  
9  _SomeWidgetState createState() => _SomeWidgetState();
10}
11
12class _SomeWidgetState extends State<SomeWidget> {
13  int count = 0;
14
15  
16  Widget build(BuildContext context) {
17    return Container(
18      child: Text('$count ${widget.fruit}'),
19    );
20  }
21}
22
23class ParentWidget extends StatelessWidget {
24  
25  Widget build(BuildContext context) {
26    return Container(
27        child: SomeWidget(fruit: 'oranges'),
28    );
29  }
30}
31

Lifecycle hooks

1class _MyComponentState extends State<MyComponent> {
2  
3  void initState() {
4    // this method is called before the first build
5    super.initState();
6  }
7
8  
9  void didUpdateWidget(MyComponent oldWidget) {
10    // this method IS called when parent widget is rebuilt
11    super.didUpdateWidget(oldWidget);
12  }
13
14   didChangeDependencies() {
15    // called when InheritedWidget updates
16    // read more here https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html
17    super.didChangeDependencies();
18  }
19
20  
21  void dispose() {
22    // called after widget was unmounted from widget tree
23    super.dispose();
24  }
25}
26

Android Ink effect

1InkWell(
2  child: Text('Button'),
3  onTap: _onTap,
4  onLongPress: _onLongPress,
5  onDoubleTap: _onDoubleTap,
6  onTapCancel: _onTapCancel,
7);
8

Detecting Gestures

1GestureDetector(
2  onTap: _onTap,
3  onLongPress: _onLongPress,
4  child: Text('Button'),
5);
6

Loading indicator

1class SomeWidget extends StatefulWidget {
2  
3  _SomeWidgetState createState() => _SomeWidgetState();
4}
5
6class _SomeWidgetState extends State<SomeWidget> {
7  Future future;
8
9  
10  void initState() {
11    future = Future.delayed(Duration(seconds: 1));
12    super.initState();
13  }
14
15  
16  Widget build(BuildContext context) {
17    return FutureBuilder(
18      future: future,
19      builder: (context, snapshot) {
20        return snapshot.connectionState == ConnectionState.done
21            ? Text('Loaded')
22            : CircularProgressIndicator();
23      },
24    );
25  }
26}
27

Platform specific code

1import 'dart:io' show Platform;
2
3if (Platform.isIOS) {
4  doSmthIOSSpecific();
5}
6
7if (Platform.isAndroid) {
8  doSmthAndroidSpecific();
9}
10

Hide status bar

1import 'package:flutter/services.dart';
2
3void main() {
4    SystemChrome.setEnabledSystemUIOverlays([]);
5}
6

Lock orientation

1import 'package:flutter/services.dart';
2
3void main() async {
4  await SystemChrome.setPreferredOrientations([
5    DeviceOrientation.portraitUp,
6  ]);
7
8  runApp(App());
9}
10

Show alert

1showDialog<void>(
2  context: context,
3  barrierDismissible: false,
4  builder: (BuildContext context) {
5    return AlertDialog(
6      title: Text('Alert Title'),
7      content: Text('My Alert Msg'),
8      actions: <Widget>[
9        FlatButton(
10          child: Text('Ask me later'),
11          onPressed: () {
12            print('Ask me later pressed');
13            Navigator.of(context).pop();
14          },
15        ),
16        FlatButton(
17          child: Text('Cancel'),
18          onPressed: () {
19            print('Cancel pressed');
20            Navigator.of(context).pop();
21          },
22        ),
23        FlatButton(
24          child: Text('OK'),
25          onPressed: () {
26            print('OK pressed');
27            Navigator.of(context).pop();
28          },
29        ),
30      ],
31    );
32  },
33);
34

Check if dev

1bool isDev = false;
2assert(isDev == true);
3
4if (isDev) {
5    doSmth();
6}
7

Navigation

1import 'package:flutter/material.dart';
2
3class FirstScreen extends StatelessWidget {
4  
5  Widget build(BuildContext context) {
6    return Center(
7      child: RaisedButton(
8        child: Text('Go to SecondScreen'),
9        onPressed: () => Navigator.pushNamed(context, '/second'),
10      ),
11    );
12  }
13}
14
15class SecondScreen extends StatelessWidget {
16  void _pushSecondScreen(context) {
17    Navigator.push(context, MaterialPageRoute(builder: (context) => SecondScreen()));
18  }
19
20  
21  Widget build(BuildContext context) {
22    return Column(
23      children: <Widget>[
24        RaisedButton(
25          child: Text('Go back!'),
26          onPressed: () => Navigator.pop(context),
27        ),
28        RaisedButton(
29          child: Text('Go to SecondScreen... again!'),
30          onPressed: () => _pushSecondScreen(context),
31        ),
32      ],
33    );
34  }
35}
36
37void main() {
38  runApp(MaterialApp(
39    initialRoute: '/',
40    routes: {
41      '/': (context) => FirstScreen(),
42      '/second': (context) => SecondScreen(),
43    },
44  ));
45}
46

Arrays

1final length = items.length;
2
3final newItems = items..addAll(otherItems);
4
5final allEven = items.every((item) => item % 2 == 0);
6
7final filled = List<int>.filled(3, 42);
8
9final even = items.where((n) => n % 2 == 0).toList();
10
11final found = items.firstWhere((item) => item.id == 42);
12
13final index = items.indexWhere((item) => item.id == 42);
14
15final flat = items.expand((_) => _).toList();
16
17final mapped = items.expand((item) => [item + 1]).toList();
18
19items.forEach((item) => print(item));
20
21items.asMap().forEach((index, item) => print('$item, $index'));
22
23final includes = items.contains(42);
24
25final indexOf = items.indexOf(42);
26
27final joined = items.join(',');
28
29final newItems = items.map((item) => item + 1).toList();
30
31final item = items.removeLast();
32
33items.add(42);
34
35final reduced = items.fold({}, (acc, item) {
36  acc[item.id] = item;
37  return acc;
38});
39
40final reversed = items.reversed;
41
42items.removeAt(0);
43
44final slice = items.sublist(15, 42);
45
46final hasOdd = items.any((item) => item % 2 == 0);
47
48items.sort((a, b) => a - b);
49
50items.replaceRange(15, 42, [1, 2, 3]);
51
52items.insert(0, 42);
53
54

Make http request

1dependencies:
2  http: ^0.12.0
3
4
1import 'dart:convert' show json;
2import 'package:http/http.dart' as http;
3
4http.get(API_URL).then((http.Response res) {
5  final data = json.decode(res.body);
6  print(data);
7});
8

Async Await

1Future<int> doSmthAsync() async {
2  final result = await Future.value(42);
3  return result;
4}
5
6class SomeClass {
7  method() async {
8    final result = await Future.value(42);
9    return result;
10  }
11}
12

JSON

1import 'dart:convert' show json;
2
3json.decode(someString);
4json.encode(encodableObject);
5

json.decode returns a dynamic type, which is probably not very useful

You should describe each entity as a Dart class with fromJson and toJson methods

1class User {
2    String displayName;
3    String photoUrl;
4
5    User({this.displayName, this.photoUrl});
6
7    User.fromJson(Map<String, dynamic> json)
8      : displayName = json['displayName'],
9        photoUrl = json['photoUrl'];
10
11    Map<String, dynamic> toJson() {
12      return {
13        'displayName': displayName,
14        'photoUrl': photoUrl,
15      };
16    }
17}
18
19final user = User.fromJson(json.decode(jsonString));
20json.encode(user.toJson());
21
22

👉 This approach is error-prone (e.g. you can forget to update map key after class field was renamed), so you can use json_serializable as an alternative;

Add json_annotation, build_runner and json_serializable to dependencies

1dependencies:
2  json_annotation: ^2.0.0
3
4dev_dependencies:
5  build_runner: ^1.0.0
6  json_serializable: ^2.0.0
7

Update the code

1import 'package:json_annotation/json_annotation.dart';
2
3part 'user.g.dart';
4
5()
6class User {
7  String displayName;
8  String photoUrl;
9
10  User({this.displayName this.photoUrl});
11
12  // _$UserFromJson is generated and available in user.g.dart
13  factory User.fromJson(Map<String, dynamic> json) {
14    return _$UserFromJson(json);
15  }
16
17  // _$UserToJson is generated and available in user.g.dart
18  Map<String, dynamic> toJson() => _$UserToJson(this);
19}
20
21final user = User.fromJson(json.decode(jsonString));
22json.encode(user); // toJson is called by encode
23
24

👉 Run flutter packages pub run build_runner build to generate serialization/deserialization code;

To watch for changes run flutter packages pub run build_runner watch

Singleton

1class Singleton {
2  static Singleton _instance;
3
4  final int prop;
5
6  factory Singleton() =>
7    _instance ??= new Singleton._internal();
8
9  Singleton._internal()
10    : prop = 42;
11}
12

Debounce

1
2Timer _debounce;
3
4if (_debounce?.isActive ?? false) _debounce.cancel();
5_debounce = Timer(const Duration(milliseconds: 500), () {
6    someFN();
7});
8
9
Chat Avatar