Flutter - Other

Better Error Screen

void main() {
  ErrorWidget.builder = (FlutterErrorDetails details) {
    bool inDebug = false;

    assert(() {
      inDebug = true;
      return true;
    }

    if(inDebug) {
      return ErrorWidget(details.exception);
    }
    return Material(
      color: Colors.green.shade200,
      child: Center(
        child: Text(details.exception.toString()),
        style: const TextStyle(
          color: Colors.white,
          fontWeight: FontWeight.bold,
          fontSize: 20
        )
      )
    );
  };

  runApp(const MyApp());
}

Cannot run with sound null safety because dependencies don't support null safety

Android Studio

Go to Run > Edit Configurations > Add Additional Run args and add --no-sound-null-safety.

Cascade Notation Method Chaining on Steroids

Cascades Notation(...) allows chaining a sequence of operations on the same object. Besides, fields (data-members) can be accessed using the same.


class Person {
  String name;
  int age:
  Person(this.name, this.age);
  void data() => print("$name is $age years old.");
}

//Without Cascade Notation
Person person = Person("Richard", 50);
person.age = 22;
person.name += " Parker";
person.data():

//Cascade Notation with Object of Person
Person("Jian", 21)
..age = 22
..name += " Yang"
..data();

//Cascade Notation with List
<String>["Natasha", "Steve", "Peter", "Tony"]
..sort()
..forEach((name) => print("\n$name"));

Cascade Operator (..)

The cascade operator (..) is another handy Dart feature that helps you streamline object initialization. Instead of referencing the object name repeatedly, you can chain multiple property assignments and method calls to the same object.

myClass
  ..property1 = value1
  ..property2 = value

Concatenate a String with itself Multiple Times

string str = 'banana';
string result = str * 4;

//result = bananabananabananabanana

Conditions In List

You can also use conditions in List. Here sad = false so cart doesn't contain Beer in it.


void main() {
  bool sad = false;
  var cart = [ 'milk', 'ghee', if (sad) 'Beer' ];
  print(cart);
}

Show Output
[milk, ghee]

Define small widgets

Do

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Column(
      children: const [
        Widget(),
        Widget(),
        Widget()
      ]
    )
  );
}


Don't

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Column(
      children: const [
        Row(
          children: const [
            Text(),
            Text()
          ]
        ),
        Padding(
          padding: EdgeInsets.all(16.0),
          child: Column(
            children: [
              Row(
                children: const [
                  Text(),
                  Text()
                ]
              ),
              Text()
            ]
          )
        )
      ]
    )
  );
}


Conclusion: Leads to code that is more readable and easier to reason about.

Device Vibration

AndroidManifest.xml

<uses-permission android:name="android.permission.VIBRATE"/>


import "package:flutter/services.dart";

MaterialButton(
  onPressed: () {
    HapticFeedback.heavyImpact();
    HapticFeedback.mediumImpact();
    HapticFeedback.lightImpact();
    //Use any one of the above
  },
  child: Text("Vibrate")
);

Don’t create a lambda when a tear-off will do

If we have a function that invokes a method with the same arguments as are passed to it, you don’t need to manually wrap the call in a lambda.\


Do

List<String> names = []

names.forEach(print);


Don't

List<String> names = []
	  
names.forEach((name) {
  print(name);
});

Don’t explicitly initialize null variables

In Dart, the variable is automatically initialized to null when its value is not specified, so is adding null is redundant and unneeded.


Do

int _item;


Don't

int _item = null;

Don't Use Function in Future Builder

Don't do this

class FutureDemoPage extends StatelessWidget {
  /// Function that will return a
  /// "string" after some time
  /// To demonstrate network call
  /// delay of [2 seconds] is used
  ///
  /// This function will behave as an
  /// asynchronous function
  Future<String> getData() {
    return Future.delayed(Duration(seconds: 2), () {
      return "I am data";
      // throw Exception("Custom Error");
    });
  }
 
  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        appBar: AppBar(
          title: Text('Future Demo Page'),
        ),
        body: FutureBuilder(
          builder: (ctx, snapshot) {
            // Checking if future is resolved or not
            if (snapshot.connectionState == ConnectionState.done) {
              // If we got an error
              if (snapshot.hasError) {
                return Center(
                  child: Text(
                    '${snapshot.error} occurred',
                    style: TextStyle(fontSize: 18),
                  ),
                );
 
                // if we got our data
              } else if (snapshot.hasData) {
                // Extracting data from snapshot object
                final data = snapshot.data as String;
                return Center(
                  child: Text(
                    '$data',
                    style: TextStyle(fontSize: 18),
                  ),
                );
              }
            }
 
            // Displaying LoadingSpinner to indicate waiting state
            return Center(
              child: CircularProgressIndicator(),
            );
          },
 
          // Future that needs to be resolved
          // inorder to display something on the Canvas
          future: getData(),
        ),
      ),
    );
  }
}


Do this

Future<String> _value;
 
  @override
  initState() {
    super.initState();
    _value = getValue();
  }

  FutureBuilder<String>(
    future: _value,
    // other arguments
  )

Double And Triple Dots

Double dots(..) i.e cascade operator

Double dots(..) is known as cascade notation (allow you to make a sequence of operations on the same object). It allows you to not repeat the same target if you want to call several methods on the same object.This often saves you the step of creating a temporary variable and allows you to write more fluid code. Normally, we use the below way to define several methods on the same object.


//before
var paint = Paint();
paint.color = Colors.black;
paint.strokeCap = StrokeCap.round;
paint.strokeWidth = 5.0;

//after
var paint = Paint()
  ..color = Colors.black
  ..strokeCap = StrokeCap.round
  ..strokeWidth = 5.0;


Triple dots(…) i.e. Spread Operator

Triple dots(…) also known as spread operator which provide a concise way to insert multiple values into a collection.You can use this to insert all the elements of a list into another list:


//before
var list = [1, 2, 3];
var list2 = [];
list2.addAll(list);

//after
var list = [1, 2, 3];
var list2 = [0, ...list];