16 Essential Dart Tips and Tricks for Flutter Developers

16 Essential Dart Tips and Tricks for Flutter Developers

·

12 min read

This lesson offers some of the best dart coding tips and methods. This will be a very useful guide for beginners in flutter development and experienced flutter developer as well. Flutter developers who would use them to produce more concise, effective code while also making the most of the Dart language.

1. Do you know? Dart implements multiplication of stringsDart

Here is a straightforward program that demonstrates how to produce a Christmas tree using string multiplication:

void main() {
  for (var i = 1; i <= 5; i++) {
    print('*' * i);
  }
}

Output

This may be used to determine whether a long string can fit into a Text widget:

Text(‘You have pushed the button this many times:’ * 5)

2. Do You Need To Run Many Futures At Once? Apply Future. Wait.

Consider the following fake API class, which provides the most recent statistics on COVID cases:

// Mock API class
class CovidAPI {
  Future getCases() => Future.value(1000);
  Future getRecovered() => Future.value(100);
  Future getDeaths() => Future.value(10);
}

3. Include A “Call” Function In Your Dart Structures To Enable Function-Like Calling.

Here is a PasswordValidator class illustration:

class PasswordValidator {
  bool call(String password) {
    return password.length > 10;
  }
}

We may define a design pattern and utilize it just like a method since the method is called call:

final validator = PasswordValidator();
// can use it like this:
validator('test');
validator('test1234');
// no need to use it like this:
validator.call('not-so-frozen-arctic');

Also, Read This Post:

How to Store Login Information Using Flutter ??

4. Do You Need To Call A Response But Only When It Is Not Null? Can Use Syntax “?.Call()”.

Let us say we get a special Flutter widget class that, whenever a specific event occurs, should fire a onDragCompleted callback:

class CustomDraggable extends StatelessWidget {
  const CustomDraggable({Key key, this.onDragCompleted}) : super(key: key);
  final VoidCallback? onDragCompleted;

  void _dragComplete() {
    // TODO: Implement me
  }
  @override
  Widget build(BuildContext context) {/*...*/}
}

To invoke the callback, we could write this code:

  void _dragComplete() {
    if (onDragCompleted != null) {
      onDragCompleted();
    }
  }

But there is a simpler way (note the use of ?.):

  Future _dragComplete() async {
    onDragCompleted?.call();
  }

5. Employing Variables As Parameters And Anonymous Procedures

Functions can be supplied as parameters to other procedures in Dart since they are first-class residents.

The following code creates an unidentified procedure and applies it to the variable sayHi:

void main() {
  final sayHi = (name) => 'Hi, $name';
  welcome(sayHi, 'Andrea');
}
void welcome(String Function(String) greet,
             String name) {
  print(greet(name));
  print('Welcome to this course');
}

Output

SayHi is then sent to a welcome function, which utilizes it together with a Function parameter to greet the user.

A function type called String Function(String) accepts a String parameter and outputs a String. The aforementioned, anonymous method can be provided primarily as an argument or through the sayHi variable because they share the same signature.

When utilizing functional operators like map, where, and reduction.

Here is an example of a straightforward function to find the square of an amount:

int square(int value) {
  // just a simple example
  // could be a complex function with a lot of code
  return value * value;
}

We may map over a list of parameters to obtain the squares:

const values = [1, 2, 3];
values.map(square).toList();

Since the map function expects the signature of a square in this case, we send it as an input. As a result, we are relieved of the necessity to enlarge it with an unnamed function:

values.map((value) => square(value)).toList();

Also, Read This Post:

How to Map a List in a Flutter Application?

6. You May Utilize Lists, Sets, And Mappings With Collection-If And Distributes

When writing your UI as code, collection-if and spreads come in quite handy.

You may also use them with mapping, though.

Take the following instance:

const addRatings = true;
const restaurant = {
  'name' : 'Pizza Mario',
  'cuisine': 'Italian',
  if (addRatings) ...{
    'avgRating': 4.3,
    'numRatings': 5,
  }
};

Here, we are defining a map of restaurants, and if addRatings is correct, we will only add the avgRating as well as numRatings key-value pairs. Additionally, we must employ the spread operator since we are adding many key-value pairs (…).

7. Do you need to null-safely iterate across a map? Utilize “.entries”:

If you have the following map:

const timeSpent = {
  'Blogging': 10.5,
  'YouTube': 30.5,
  'Courses': 75.2,
};

Here’s how to create a loop that executes some function while utilizing every key-value pair:

for (var entry in timeSpent.entries) {
  // do something with keys and values
  print('${entry.key}: ${entry.value}');
}

You have better accessibility to most of the key-value pairs by executing the following on the entries variable.

Compared to that, this is clearer and less prone to mistakes:

for (var key in timeSpent.keys) {
  final value = timeSpent[key]!;
  print('$key: $value');
}

Due to Dart’s inability to verify that a value occurs for a particular key, the code following necessitates the usage of the assertion operator (!) when retrieving the values.

8. For More Practical Apis, Use Called Builders And Initializer Lists.

Let us say you want to develop an object that embodies the value of a temperature.

With two specified constructors, you could keep your class API clear and handle both Celsius and Fahrenheit:

class Temperature {
  Temperature.celsius(this.celsius);
  Temperature.fahrenheit(double fahrenheit)
    : celsius = (fahrenheit - 32) / 1.8;
  double celsius;
}

This class utilizes an initializer sequence to translate Fahrenheit to Celsius and only requires one saved variable to indicate the temperature.

As a result, you may specify temperature data in the following way:

final temp1 = Temperature.celsius(30);
final temp2 = Temperature.fahrenheit(90);

9. Getters and setters

Celsius is designated as a stored element in the Temperature type mentioned above.

However, some users may like setting or obtaining the temp in Fahrenheit.

Getters and setters, which let you define calculated variables, make this simple. Here is the class as of today:

class Temperature {
  Temperature.celsius(this.celsius);
  Temperature.fahrenheit(double fahrenheit)
    : celsius = (fahrenheit - 32) / 1.8;
  double celsius;
  double get fahrenheit
    => celsius * 1.8 + 32;
  set fahrenheit(double fahrenheit)
    => celsius = (fahrenheit - 32) / 1.8;
}

This makes getting or setting the temperature in Fahrenheit or Celsius simple:

final temp1 = Temperature.celsius(30);
print(temp1.fahrenheit);
final temp2 = Temperature.fahrenheit(90);
temp2.celsius = 28;

To make your classes more effective, utilize named constructors, getters, and setters.

10. For Unnecessary Function Parameters, Use Underscores

We frequently employ widgets in Flutter that accept function parameters. ListView.builder is a typical illustration of this:

class MyListView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemBuilder: (context, index) => ListTile(
        title: Text('all the same'),
      ),
      itemCount: 10,
    );
  }
}

Output

The (context, index) parameters in the itemBuilder are not being used in this instance. In their substitute, we can use underscores:

ListView.builder(
  itemBuilder: (_, __) => ListTile(
    title: Text('all the same'),
  ),
  itemCount: 10,
)

Please note that the two parameters ( and __) differ because they each use a distinct identifier.

Also, Read This Post:

ListView Widget – Flutter Widget Guide By Flutter Agency

11. Do you require a singleton (also known as a single instance) class? Employ a private function Object() { [native code] } and a static instance variable

A singleton’s ability to exist only once throughout your programme is its most crucial characteristic. A file system, for example, may be modeled using this.

// file_system.dart
class FileSystem {
  FileSystem._();
  static final instance = FileSystem._();
}

Using the_syntax, you may declare a named function Object() { [native code] } and make it private in Dart to create a singleton.

After that, you may employ it to produce a single static final version of your class.

And as a consequence, the instance variable will be the only way for any code in many other files to retrieve this class:

// some_other_file.dart
final fs = FileSystem.instance;
// do something with fs

Note: If you are not careful, singletons can cause many issues. Be careful to be aware of the drawbacks before utilizing them.

12. Do you require a variety of unique items? Instead of using a List, use a Set

A List is the most frequently used collection type in Dart.

Lists, however, can include duplicate entries, which is certainly not what we desire:

const citiesList = [
  'London',
  'Paris',
  'Rome',
  'London',
];

Anytime we want a group of distinctive values, we may use a Set (notice the usage of final):

// set is final, compiles
final citiesSet = {
  'London',
  'Paris',
  'Rome',
  'London', // Two elements in a set literal shouldn't be equal
};

Since London is mentioned twice in the code above, a notice is produced. We encounter an issue, and our program does not compile if we attempt to perform the same task using a const set:

// set is const, doesn't compile
const cities

Set = {
  'London',
  'Paris',
  'Rome',
  'London', // Two elements in a constant set literal can't be equal
};

When dealing with sets, we can utilize APIs like union, variance, and overlap.

citiesSet.union({'Delhi', 'Moscow'});
citiesSet.difference({'London', 'Madrid'});
citiesSet.intersection({'London', 'Berlin'});

Consider incorporating a set and decide if you want the things in your collection to be unique before you start collecting.

Also, Read This Post:

Flutter Singleton Pattern – An Ultimate Guide in 2023

13. Using The Words Try, On, Receive, Rethrow, And Finally

When utilizing Future-based APIs that might generate an exception when something turns out badly, try and catch is recommended.

To fully illustrate how to utilize them, consider the following entire example:

Future printWeather() async {
  try {
    final api = WeatherApiClient();
    final weather = await api.getWeather('London');
    print(weather);
  } on SocketException catch (_) {
    print('Could not fetch data. Check your connection.');
  } on WeatherApiException catch (e) {
    print(e.message);
  } catch (e, st) {
    print('Error: $e\nStack trace: $st');
    rethrow;
  } finally {
    print('Done');
  }
}

A few points:

  • To handle instances of various sorts, you can use numerous clauses.

  • You can add a fallback capture clause to handle any issues that do not fit one of the aforementioned kinds.

  • The current kinds as mentioned above, up the callback function as mentioned above while keeping the stack trace using a rethrow command.

  • Regardless of whether the Future was successful or unsuccessful, you may use it finally to execute some code after it has been finished.

Make careful to handle objections as necessary if you are utilizing or creating Future-based APIs.

14. Typical Future Builders

The Future.delayed, Future.value, and Future.error factory constructors from the Dart Future type are quite useful.

To build a Future that awaits for a specific delay, we may use Future.delayed to build a future that awaits a specific delay. An optional nameless function is indeed the second parameter, which you may use to raise an error or finish with a value:

await Future.delayed(Duration(seconds: 2), () => 'Latte');

However, there are occasions when we wish to design a Future that ends right away:

await Future.value('Cappuccino');
await Future.error(Exception('Out of milk'));

To finish properly with a value or to finish with an error, we may use the Future.value or Future.error methods.

You may simulate the reply from your Future-based APIs using these constructors. When creating dummy classes for your test code, this is helpful.

Also, Read This Post:

How to implement Rest API in Flutter?

15. Constructors of Common Stream

Additionally, the Stream class has a few useful constructors. Here are some of the most typical:

Stream.fromIterable([1, 2, 3]);
Stream.value(10);
Stream.empty();
Stream.error(Exception('something went wrong'));
Stream.fromFuture(Future.delayed(Duration(seconds: 1), () => 42));
Stream.periodic(Duration(seconds: 1), (index) => index);
  • To generate a stream from a list of values, use the Stream.fromIterable method.

  • If you just have one value, use Stream.value. To generate an empty stream, use Stream.empty.

  • To construct a stream that includes an error value, use Stream.error.

  • Create a stream using Stream.fromFuture that will only have one value and be available after the future is finished.

  • Create a recurring stream of events by using Stream.periodic.

  • The duration between events may be set, and an unnamed function can be used to produce each value based on its position in the stream.

16. Generators: Sync and Async

A synchronous motor in Dart is considered as a measure that outputs an Iterable:

Iterable count(int n) sync* {
  for (var i = 1; i <= n; i++) {
    yield i;
  }
}

This uses the sync* syntax. Inside the function we can “generate” or yield multiple values. As soon as the function is finished, these will be presented as an Iterable.

An asynchronous generator, on the other side, is a function that provides a Stream:

Stream countStream(int n) async* {
  for (var i = 1; i <= n; i++) {
    yield i;
  }
}

This uses this async* syntax. Inside the function we can yield values just like in the synchronous case.

But given that this is an asynchronous generator, we may wait on Future-based APIs if we so choose:

Stream countStream(int n) async* {
  for (var i = 1; i <= n; i++) {
    // dummy delay - this could be a network request
    await Future.delayed(Duration(seconds: 1));
    yield i;
  }
}

Conclusion

You now know the best Dart advice for Flutter devs. Utilize them to enhance your Flutter apps’ code. However, Dart is the powerful and robust language used with Flutter for developing mobile apps. They make use of hot reload, plugins, packages, libraries, widgets, mixin classes, and so on to make their app coding efficient and effective for projects. Hence, with these top 16 tips and tricks, programmers can easily create amazing apps.

These Dart tips and tricks are useful and helpful when you want to expand your app project to get a wider audience for your brand. You can learn these tips and tricks and upskill yourself in flutter development. Also, If you are looking for a flutter app development agency that can assist you in building your world-class mobile app. Get in touch with us for further discussion.

Frequently Asked Questions (FAQs)

1. Why is the Flutter framework so fast?

It is the client-optimized programming language for fast-performing mobile apps on distinct platforms. Dart is AOT complied to faster, predictable, native code, allowing to write whole Flutter code in Dart. Hence, it made Flutter extremely fast and customizable.

2. Does Flutter generate HTML code?

It maps HTML/CSS code snippets in its Flutter or Dart equivalents. Flutter s the open-source framework for creating cross-platform apps using a Dart programming language. Dart also uses JavaScript for programming.

3. Who is faster, Java or Flutter?

Flutter is identified because of its fast performance and smooth graphics, and it is an ideal choice for high-performance applications. On the other hand, Java gives a great choice for performance as it delivers the scalability and security of apps.