Not just Flutter/Dart, learning the essentials of any Programming Language is the mandatory section for every Developer. However, there seems to be boredom in learning this stuff and some people try very hard to get past this. But this blog post will make this stuff easy and with better readability to catch up with the fundamental aspects of Dart Language. So let’s begin: Mastering Flutter/Dart Keywords – An Interesting Guide for Developers.
We shall be learning some of the popular keywords in Dart Language with examples, snippets, and so on…
Note: In the post-fix, assume there is a word keyword
since it would be weird to write it down for each word.
Table of Contents
– static
The static keyword is all about declaring variables, methods, and getters/setters that are associated with the class itself rather than with instances of the class.
When we declare a variable as static in a class, it means that it is shared among all instances of the class. Moreover, static variables are initialized only once, and their values persist across multiple instances of the class. Furthermore, they can be accessed without creating an object of the class.
Here’s the code snippet
class Blogs {
static String blogTitle = 'Flutter Driectory is a good choice for Flutter
stuff.';
}
main() {
print(Blogs.blogTitle);
}
In addition, static methods also follow the same procedure, but one thing here needs to be addressed. i.e. static methods cannot access instance variables or methods that are not associated with any specific instance. Such as:
class Blogs {
static String blogFunction() {
return "Flutter Directory is the best source of knowlege for Flutter
Engineers.";
}
}
main() {
print(Blogs.blogFunction());
}
So, by combining all, static members can be useful for sharing data or providing utility methods that don’t depend on instance-specific states.
– factory
A factory keyword
is used to declare a factory constructor, which is a special type of constructor that is used to return an instance of the class using custom logic instead of creating a new object. Apart from this, a factory constructor can be used to implement object caching, return a different subtype of class, or perform any other instantiation logic.
Here is a code snippet
class MarksScored {
final int value;
MarksScored._(this.value);
factory MarksScored(int marks) {
if(marks >= 90) {
return MarksScored._(marks);
}
else {
return MarksScored._(0);
}
}
}
void main() {
MarksScored scored = MarksScored(97);
print("Total marks ${scored.value}");
MarksScored ZeroScored = MarksScored(0);
print("Total marks ${ZeroScored.value}");
}
Additionally, to keep it simple, you need to remember two points:
The return statement is used in the same instance.
Factory doesn’t support this keyword.
– var
When using var
, Dart infers (deduces) the type of variable based on the assigned value.
Here’s an example snippet
main() {
var title = 'Flutter Directory';
var isOnlyFlutter = true;
var blogCount = 11;
var audienceRetention = 99.78;
print(title);
print(isOnlyFlutter);
print(blogCount);
print(audienceRetention);
}
w.r.t the above snippet, the variable:
title
= inferred as type string
isOnlyFlutter
= inferred as type bool
blogCount
= inferred as type int
audienceRetention
= inferred as type double
Furthermore, these values are determined at compile-time and remain the same throughout the variable’s scope. Since Dart is a statically typed Programming Language (where variable types are known at compile time), even though the type is implicit, it is still verified at compile time to ensure type safety.
However, declaring var
cannot be considered the best approach. It is always good practice to use explicit keywords to ensure better readability and code clarity. Moreover, it also avoids potential issues regarding type inferencing.
– dynamic
Declaring variables with dynamic
types
is unknown until runtime and variables can hold onto any type.
Here’s the code snippet
void main() {
dynamic value = 'Hello'; // Variable with dynamic type
print(value); // Output: Hello
value = 10; // Assigning an integer value
print(value); // Output: 10
value = true; // Assigning a boolean value
print(value); // Output: true
}
Note: Dart does not support static type checking on dynamic variables, and the type of variable is determined at runtime based on the assigned value.
Using dynamic
has its advantages, such as you don’t get compile-time checking vis-à-vis early error detection, etc. Instead, type errors might occur at runtime, if compatible operations or assignments are performed on the dynamic variable.
However, dynamic keywords
can be useful in certain scenarios:
- Dealing with dynamic Data Structures
- Interacting with API calls
- An explicit need to work with different types
So, dynamic types
should be avoided and instead should be replaced with statically typed types.
– implements
Implements
indicate that a class is implementing one or more interfaces in Dart.
Interface – defined as a set of method signatures that a class must implement.
Here is the code snippet illustrating the usage of implements.
// Interface definition
abstract class Animal {
void makeSound();
}
// Class implementing an interface
class Dog implements Animal {
@override
void makeSound() {
print('Woof!');
}
}
void main() {
var dog = Dog();
dog.makeSound(); // Output: Woof!
}
However, it’s important to understand that Dart does not have explicit interface types like other languages such as Java and so on… Instead, any class can act as an interface and other classes can use them via the implements’ keyword. This allows for flexible and ad-hoc implementation.
– with and mixins
I am wrapping with
and mixins
in the same section because one is dependent on another.
with
is used to allow the use of one or more mixins that a class will use. Whereas, mixins
are a way to reduce code in multiple classes without the need for traditional inheritance.
Here’s a code snippet illustrating the use of both.
// Mixin definition
mixin Swimming {
void swim() {
print('Swimming...');
}
}
mixin Flying {
void fly() {
print('Flying...');
}
}
// Class using mixins
class Duck with Swimming, Flying {
void display() {
print('I am a duck.');
}
}
void main() {
var duck = Duck();
duck.display(); // Output: I am a duck.
duck.swim(); // Output: Swimming...
duck.fly(); // Output: Flying...
}
Note: It’s important to remember that the order of mixins specified under with keyword matters a lot.
If multiple mixins define the same method, then the method of rightmost mixins takes precedence. Additionally, mixins cannot have constructors, and a class can use multiple mixins by separating them with commas after the with
keyword.
– const
The const keyword is used to declare compile-time constant values. In addition, this remains constant throughout the program execution.
The code snippet further illustrates:
const int audienceCount = 450;
const String comment = "This is marvelous though!";
and so on...
Apart from this, the const keyword
can be used for any sort of collection of literals too, such as lists, maps, and sets
.
const myList = [1, 2, 3];
const mySet = {1, 2, 3};
const myMap = {'a': 1, 'b': 2};
Furthermore, const keywords
can also be used with final and static values to declare variables for that particular behavior.
static const int x = 5;
final const double pi = 3.14;
So, declaring a variable const
means more code and memory optimizations and performance enhancements.
– final
The final keyword
is used to assign values once at runtime
. Once assigned, its value cannot be changed till program execution. Such that:
final int x = 5;
final String name = 'John';
Moreover, as per the above heading regarding the const keyword, the same approach can be used for the final too, but with this minor difference, we talked about in the first line.
A similar example snippet
final List<int> numbers = [1, 2, 3];
final Set<String> names = {'John', 'Jane'};
final Map<String, int> scores = {'John': 90, 'Jane': 85};
Just like const
, the final keyword
also help in code clarity and improve readability.
– mounted
Mounted is not potentially a Dart keyword, but instead, it is used in the context of Flutter.
It is used by Flutter’s state to determine the availability of the widget and acts as a flag.
Here is an illustration
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
bool _isLoading = false;
@override
void initState() {
super.initState();
_loadData();
}
void _loadData() async {
setState(() {
_isLoading = true;
});
// Simulate an asynchronous operation
await Future.delayed(Duration(seconds: 2));
if (mounted) {
setState(() {
_isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: _isLoading ? CircularProgressIndicator() : Text('Data Loaded'),
),
);
}
}
In the above snippet, the mounted entity is used to check if the widget is still mounted (available) so that the bool _isLoading is loaded or not
.
Additionally, mounted helps to avoid potential errors, especially when working with asynchronous operations such as async and await, or when the state of the widget might change while the operation is in progress.
– typedef
A typedef keyword
is used to create function type aliases
. It allows defining custom names for a function type, making code easy and more expressive to read.
Here is an illustration snippet
typedef String MyFunction(int a, String b);
void main() {
MyFunction myFunction = (int a, String b) {
return '$a $b';
};
String result = myFunction(42, 'Hello');
print(result); // Output: 42 Hello
}
Using typedef promotes code clarity, especially when dealing with complex function signatures or just giving them meaningful names. Secondly, it promotes type safety, code reuse ability, and so on.
– extension
The extension method
is used to add functionality to existing classes or types without changing their original identity.
— Defining an extension
extension nameOfExtension on type {
type get nameOfMethod => // what function you wanna perfom
}
Here is an illustration snippet
extension DateFormat on DateTime {
String get formatter => this.year.toString();
}
void main() {
print(DateTime.now().formatter); // output => 2023
}
Moreover, getting the complete height of a device via MediaQuery wrapped under extension.
extension MediaQueryEx on BuildContext {
double allHeight get => toDouble();
}
-------------
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
height: context.allHeight,
child: Column(
children: [
],
),
);
}
}
Using extension methods, no doubt is the best invention by the Flutter team to optimize our existing code bases and maintain code readability.
– enum
In Dart, enums
are special datatypes that represent a set of constant values. It provides a way to define a discrete set of named values, which can be used to make code more readable and maintainable.
Code snippet
enum Shapes {
circle,
rectangle,
square,
diagonal
}
void main() {
Shapes shape = Shapes.diagonal;
print(shape);
}
Apart from this, enums are also comparable.
enum Shapes {
circle,
rectangle,
square,
diagonal
}
void main() {
Shapes shape = Shapes.diagonal;
print(shape == Shapes.diagonal);
print(shape == Shapes.square);
}
In conclusion, enums are a great way to make code readable and reduce code complexity.
– async, await
async and await
comes under Asynchronous Programming. A detailed description can be found in the link below:
Furthermore, this link may be useful too.
– async*, yield
async* and yield
comes under streams. In addition, I would recommend the same link which is provided above as it contains a detailed scenario of streams and their types.
Attaching this link will surely be helpful for you.
Thanks for your precious time!
Wrapping up
In this blog post: Mastering Flutter/Dart Keywords – An Interesting Guide for Developers, I tried my best, up to my limited knowledge, to make this fit for a beginner to a pro-level developer.
I would love to have your feedback on this topic, and if you think something has been missed out, you can jot it down in the comment box of this blog post.
Furthermore, connect with me on the:
GitHub Repositories, and YouTube Channel.
Read out previous blogs – Click here