Flutter - Other

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];

Download files with Cloud Storage

Create a Reference

To download a file, first create a Cloud Storage reference to the file you want to download.


You can create a reference by appending child paths to the root of your Cloud Storage bucket, or you can create a reference from an existing gs:// or https:// URL referencing an object in Cloud Storage.

// Create a storage reference from our app
final storageRef = FirebaseStorage.instance.ref();

// Create a reference with an initial file path and name
final pathReference = storageRef.child("images/stars.jpg");

// Create a reference to a file from a Google Cloud Storage URI
final gsReference =
    FirebaseStorage.instance.refFromURL("gs://YOUR_BUCKET/images/stars.jpg");

// Create a reference from an HTTPS URL
// Note that in the URL, characters are URL escaped!
final httpsReference = FirebaseStorage.instance.refFromURL(
    "https://firebasestorage.googleapis.com/b/YOUR_BUCKET/o/images%20stars.jpg");


Download in memory

Download the file to a UInt8List with the getData() method. This is the easiest way to download a file, but it must load the entire contents of your file into memory. If you request a file larger than your app's available memory, your app will crash. To protect against memory issues, getData() takes a maximum amount of bytes to download. Set the maximum size to something you know your app can handle, or use another download method.

final islandRef = storageRef.child("images/island.jpg");

try {
  const oneMegabyte = 1024 * 1024;
  final Uint8List? data = await islandRef.getData(oneMegabyte);
  // Data for "images/island.jpg" is returned, use this as needed.
} on FirebaseException catch (e) {
  // Handle any errors.
}


Download to a local file

The writeToFile() method downloads a file directly to a local device. Use this if your users want to have access to the file while offline or to share the file in a different app. writeToFile() returns a DownloadTask which you can use to manage your download and monitor the status of the download.

final islandRef = storageRef.child("images/island.jpg");

final appDocDir = await getApplicationDocumentsDirectory();
final filePath = "${appDocDir.absolute}/images/island.jpg";
final file = File(filePath);

final downloadTask = islandRef.writeToFile(file);
downloadTask.snapshotEvents.listen((taskSnapshot) {
  switch (taskSnapshot.state) {
    case TaskState.running:
      // TODO: Handle this case.
      break;
    case TaskState.paused:
      // TODO: Handle this case.
      break;
    case TaskState.success:
      // TODO: Handle this case.
      break;
    case TaskState.canceled:
      // TODO: Handle this case.
      break;
    case TaskState.error:
      // TODO: Handle this case.
      break;
  }
});


Download Data via URL

If you already have download infrastructure based around URLs, or just want a URL to share, you can get the download URL for a file by calling the getDownloadURL() method on a Cloud Storage reference.

final imageUrl =
    await storageRef.child("users/me/profile.png").getDownloadURL();


Handle Errors

There are a number of reasons why errors may occur on download, including the file not existing, or the user not having permission to access the desired file. More information on errors can be found in the Handle Errors section of the docs.

final islandRef = storageRef.child("images/island.jpg");

final appDocDir = await getApplicationDocumentsDirectory();
final filePath = "${appDocDir.absolute}/images/island.jpg";
final file = File(filePath);

final downloadTask = islandRef.writeToFile(file);
downloadTask.snapshotEvents.listen((taskSnapshot) {
  switch (taskSnapshot.state) {
    case TaskState.running:
      // TODO: Handle this case.
      break;
    case TaskState.paused:
      // TODO: Handle this case.
      break;
    case TaskState.success:
      // TODO: Handle this case.
      break;
    case TaskState.canceled:
      // TODO: Handle this case.
      break;
    case TaskState.error:
      // TODO: Handle this case.
      break;
  }
});