This is the 20th day of my participation in the August More Text Challenge. In preparation for the Nuggets’ August challenge, I’m bringing you my dart-related tips from development, the second season of # DART 7 Tips
8. Use named constructors and initialization lists for a more ergonomic API.
Suppose you want to declare a class that represents a temperature value.
You can have your class API explicitly support two constructors named Celsius and Fahrenheit:
class Temperature { Temperature.celsius(this.celsius); Temperature. Fahrenheit (double Fahrenheit) : Celsius = (Fahrenheit - 32) / 1.8; double celsius; }Copy the code
This class requires only one storage variable to represent the temperature and uses an initialization list to convert Fahrenheit to Celsius.
This means you can declare a temperature value like this:
final temp1 = Temperature.celsius(30);
final temp2 = Temperature.fahrenheit(90);
Copy the code
9. Getter and setter
In the class above Temperature, Celsius is declared as a storage variable.
But users may prefer to get or set the temperature in degrees Fahrenheit.
This can be easily done using getters and setters, which allow you to define computed variables. Here’s the updated lesson:
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) / 1.8; }Copy the code
This makes it easy to get or set the temperature using Fahrenheit or Celsius:
final temp1 = Temperature.celsius(30);
print(temp1.fahrenheit);
final temp2 = Temperature.fahrenheit(90);
temp2.celsius = 28;
Copy the code
Bottom line: Use named constructors, getters, and setters to improve class design.
10. Underline unused function arguments
In Flutter, we often use widgets with function parameters. A common example is listView.builder:
class MyListView extends StatelessWidget { @override Widget build(BuildContext context) { return ListView.builder( itemBuilder: (context, index) => ListTile( title: Text('all the same'), ), itemCount: 10, ); }}Copy the code
In this case, we do not use the itemBuilder argument (context, index). So we can replace them with underscores:
ListView.builder(
itemBuilder: (_, __) => ListTile(
title: Text('all the same'),
),
itemCount: 10,
)
Copy the code
Note: These two parameters are different (_
and__
) because they areA separate identifier.
11. Need a class that can only be instantiated once? Use static instance variables with private constructors.
The most important feature of a singleton is that there can be only one instance of it in the entire program. This is useful for modeling things like file systems.
// file_system.dart
class FileSystem {
FileSystem._();
static final instance = FileSystem._();
}
Copy the code
To create a singleton in Dart, you can declare a named constructor and make it private using the _ syntax.
You can then use it to create a static final instance of the class.
Therefore, any code in other files can only access this class through the instance variable:
// some_other_file.dart
final fs = FileSystem.instance;
// do something with fs
Copy the code
Note: Final can cause many problems if you are not careful. Make sure you understand their disadvantages before using them.
12. Need to collect unique sets? Use collections instead of lists.
The most common collection type in Dart is List.
But lists can have duplicate items, and sometimes that’s not what we want:
const citiesList = [
'London',
'Paris',
'Rome',
'London',
];
Copy the code
We can Set using a when we need a unique Set of values (note the use of final) :
// set is final, compiles
final citiesSet = {
'London',
'Paris',
'Rome',
'London', // Two elements in a set literal shouldn't be equal
};
Copy the code
The code above generates a warning because London is included twice. If we tried to do the same for constSet, we would get an error and our code would not compile:
// set is const, doesn't compile
const citiesSet = {
'London',
'Paris',
'Rome',
'London', // Two elements in a constant set literal can't be equal
};
Copy the code
When we work with Taiwan, we can get useful apis like Union, difference, and intersection:
citiesSet.union({'Delhi', 'Moscow'});
citiesSet.difference({'London', 'Madrid'});
citiesSet.intersection({'London', 'Berlin'});
Copy the code
Bottom line: When you create a collection, ask yourself if you want its items to be unique, and consider using a collection.
13. How to use try, on, catch, rethrow, finally
Try and catch is ideal when using Future-based apis, which may throw an exception if something goes wrong.
Here’s a complete example of how to make the most of them:
Future<void> 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'); }}Copy the code
Some considerations:
- You can add more than one
on
Clause to handle different types of exceptions. - You can use rollback
catch
Clause to handle all exceptions that do not match any of the above types. - You can use
rethrow
Statement throws the current exception up the call stack,Keep the stack trace. - You can use
finally
inFuture
Run some code when you’re done, whether it succeeds or fails.
If you are using or designing future-based apis, be sure to handle exceptions as needed.
14. Common Future constructors
DartFuture class with some convenient factory constructor: Future., of the Future. The value and Future of the error.
We can future.delayed to create a Future that waits for a certain amount of delay. The second argument is an (optional) anonymous function that you can use to complete a value or throw an error:
await Future.delayed(Duration(seconds: 2), () => 'Latte');
Copy the code
But sometimes we want to create a Future that’s done immediately:
await Future.value('Cappuccino');
await Future.error(Exception('Out of milk'));
Copy the code
We can do this successfully with future. value as a value, or future. error as an error.
You can use these constructors to simulate responses from future-based apis. This is useful when writing mock classes in your test code.
15. Generic flow constructor
The Stream class also comes with some handy constructors. Here are the most common:
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);
Copy the code
- Used for a list of slave values
Stream.fromIterable
To create aStream
. - use
Stream.value
If you only have one value. - Used for
Stream.empty
Create an empty stream. - Used for
Stream.error
Create a stream that contains the error value. - Used for
Stream.fromFuture
Create a flow that contains only one value that will be available on future completion. - Used for
Stream.periodic
Create a periodic stream of events. You can specify aDuration
Is the time interval between events, and specifies an anonymous function to generate each value given its index in the stream.
16. Synchronous and asynchronous generators
In Dart, we can define the synchro generator as a function Iterable that returns:
Iterable<int> count(int n) sync* { for (var i = 1; i <= n; i++) { yield i; }}Copy the code
This uses sync* syntax. Inside a function, we can “generate” or yield multiple values. These will be returned by Iterable when the function completes.
An asynchronous generator, on the other hand, is a function Stream that returns a:
Stream<int> countStream(int n) async* { for (var i = 1; i <= n; i++) { yield i; }}Copy the code
This uses this async* syntax. Inside the function, we can yield just as we would in the synchronous case.
But we can use await future-based API if we want, because this is an asynchronous generator:
Stream<int> 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; }}Copy the code