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
2Hello 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}
20Stateless 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}
15Required 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}
19Stateful 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}
34Combining 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}
31Lifecycle 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}
26Android Ink effect
1InkWell(
2 child: Text('Button'),
3 onTap: _onTap,
4 onLongPress: _onLongPress,
5 onDoubleTap: _onDoubleTap,
6 onTapCancel: _onTapCancel,
7);
8Detecting Gestures
1GestureDetector(
2 onTap: _onTap,
3 onLongPress: _onLongPress,
4 child: Text('Button'),
5);
6Loading 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}
27Platform specific code
1import 'dart:io' show Platform;
2
3if (Platform.isIOS) {
4 doSmthIOSSpecific();
5}
6
7if (Platform.isAndroid) {
8 doSmthAndroidSpecific();
9}
10Hide status bar
1import 'package:flutter/services.dart';
2
3void main() {
4 SystemChrome.setEnabledSystemUIOverlays([]);
5}
6Lock orientation
1import 'package:flutter/services.dart';
2
3void main() async {
4 await SystemChrome.setPreferredOrientations([
5 DeviceOrientation.portraitUp,
6 ]);
7
8 runApp(App());
9}
10Show 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);
34Check if dev
1bool isDev = false;
2assert(isDev == true);
3
4if (isDev) {
5 doSmth();
6}
7Navigation
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}
46Arrays
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
54Make http request
1dependencies:
2 http: ^0.12.0
3
41import '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});
8Async 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}
12JSON
1import 'dart:convert' show json;
2
3json.decode(someString);
4json.encode(encodableObject);
5json.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
7Update 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}
12Debounce
1
2Timer _debounce;
3
4if (_debounce?.isActive ?? false) _debounce.cancel();
5_debounce = Timer(const Duration(milliseconds: 500), () {
6 someFN();
7});
8
9