FAQ¶
Is it a must to always use MaterialApp
for as the base for every app?¶
While it is not strictly required to use MaterialApp
in every Flutter application, it is highly recommended and serves as the root widget for most applications, especially those that implement Material Design.
The MaterialApp
widget serves several key roles, including:
- It acts as a wrapper around your application and provides many features that follow the Material Design guidelines.
- It includes a Navigator, which manages routing and navigation for the application. This includes displaying other widgets based on routes, handling back navigation, and transition animations.
- It provides themes for the application, allowing you to control the visual appearance including colors, text styles, etc.
- It offers localization, accessibility support, and many more built-in services that would be cumbersome to implement on your own.
In apps that don't need these services, or prefer a different design language, you could use the more bare-bones WidgetsApp
as your root widget or even build your own custom root widget. However, for most typical Flutter apps, MaterialApp
provides a foundation that's both easy to use and feature-rich.
Why need the const
to be added to widgets?¶
In Dart and Flutter, the const
keyword is used to represent a compile-time constant. Values which are assigned as const
are determined at compile-time and cannot be changed once assigned.
Adding const
to your Flutter widgets tells the framework that the widget is immutable and can be efficiently redrawn by directly reusing the previous widget when performed in a rebuild operation.
For example, instead of creating and rendering Text widget every time screen rebuilds:
You can use:
This const
keyword will tell Flutter that this widget will never change, and Flutter can optimize by reusing it from cache instead of creating a new Widget.
Here are the general rules when to use const
in Flutter:
- If the widget and its entire subtree doesn't depend on any external data or state, you should add
const
to the widget. - Do not use const if any value within that widget can change dynamically through any sort of state management (like setState, Provider, Bloc, etc), or data received from server, or user input, and it requires the widget to rebuild.
const
can only work with data that is immediately available at compile-time. It cannot work with data that would only be available at runtime.
When to use stateless and when to use stateful?¶
In Flutter, widgets form the fundamental building blocks of the UI and can be divided into two types:
- Stateless Widgets are static widgets that remain unmodified once they have been built. They describe a part of the user interface which can depend on configuration information (constructor arguments) and can change only if their parent changes. They do not store mutable data. Examples are
Text
,Icon
, andRaisedButton
.
When to use: If your widget only needs to display some data and doesn't need to manage any state itself, it can be a StatelessWidget.
- Stateful Widgets are dynamic and store mutable data. They are used when the part of the UI can change dynamically. A StatefulWidget can be used any time the widget might need to change: for example, a Widget that animates, a Widget that changes shape or color when a user interacts with it. The widget’s state is created, updated, and destroyed by the framework using the StatefulWidget’s createState, State.build, and State.dispose.
When to use: If your widget needs to maintain some kind of state, like user input, configuration, or read from a database, it can be a StatefulWidget.
Remember: While making the decision between Stateless and Stateful, it is important to consider the lifecycle of your widget and whether or not it will need to maintain and change data over time.
What lifecycle process of a stateful widget?¶
A Stateful widget in Flutter has a lifecycle that governs its behavior. Here are the main stages of the lifecycle:
-
createState(): This is the first method called when you instantiate a
StatefulWidget
. It returns a new state object. -
initState(): This is the first method called after the State object is created. This method is only called once in the lifecycle of a state object. It is typically overridden to perform one-time setup that requires context, such as reading shared preferences or database, listening to streams, etc.
-
didChangeDependencies(): Called when a dependency changes. If you refer to an
InheritedWidget
in yourbuild
method, this method is called when that widget changes. -
build(): This method is called whenever the UI needs to be rendered. It's called often to redraw the UI, so keep it efficient. It's required and needs to return a Widget.
-
didUpdateWidget(Widget oldWidget): Flutter calls this method whenever it rebuilds your
StatefulWidget
with new configuration information. You might need to update your state if you depend on your widget's configuration. -
dispose(): Just before destroying the state object, Flutter calls this method. You can override it to unsubscribe from any streams, cancel any network requests or clean up resources used by your state object.
Remember that StatefulWidgets
are immutable, but the separate State
object persists over the lifetime of the widget.
What exactly happen for setState()
?¶
In Flutter, the setState
method is a crucial function when working with StatefulWidget
. It signals to the Flutter framework that the internal state of the widget has changed in a way that requires the widget to be rebuilt.
Here's a detailed explanation of the purpose of setState
in StatefulWidget
:
Purpose of setState
¶
- Trigger a Rebuild:
- When
setState
is called, it schedules a rebuild of the widget, ensuring that any changes to the state are reflected in the UI. -
This is necessary because Flutter's rendering process is based on the concept of immutability; the UI doesn't automatically update when the state changes. Instead, the framework needs to be explicitly informed that it needs to repaint.
-
Update the State:
- The
setState
method takes a callback function where you can update the internal state variables of your widget. - The changes made inside the
setState
callback are then applied, and the framework knows to render the widget again with these new state values.
How setState
Works¶
- Mutate the State:
-
Inside the
setState
callback, you modify the state variables. -
Schedule a Rebuild:
-
Calling
setState
notifies the Flutter framework that it should trigger a rebuild of the widget. -
Rebuild the Widget:
- During the next build phase, the framework calls the
build
method of the widget, creating a new widget tree that reflects the updated state.
Syntax¶
Any time you change a variable or data inside your StatefulWidget
and you want to reflect the changes in your UI, you should wrap that change inside the setState
method.
Here is a simple example:
class MyStatefulWidget extends StatefulWidget {
@override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _count = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Counter app')),
body: Center(
child: Text('Count: $_count'),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
setState(() {
_count++;
});
},
),
);
}
}
In the above code, every time the floating action button is pressed, _count
is incremented. However, just incrementing _count
would not cause the Text
widget to visually update with the new count.
Wrapping _count++
with setState
tells the Flutter framework that it needs to rebuild the widget. Flutter will then run this widget's build method and draw the updated count on the screen.
What is the different between pushNamed()
vs pushReplacementNamed()
¶
Imagine that each time, only one screen is able to be shown and you can either have a screen fully replace with another screen or you have a screen "stack on top" of the previous screen
In Flutter, the Navigator class provides various methods to manage the stack of routes (screens or pages) in your application. Two such methods are pushNamed and pushReplacementNamed. They have different behaviors in terms of how they manipulate the navigation stack.
pushNamed
¶
pushNamed
is used to push a new route onto the navigator stack, which means it opens a new screen and keeps the current screen in the stack. This allows the user to navigate back to the previous screen using the back button.
Use Case: When you want to navigate to a new screen but still allow the user to go back to the previous screen
pushReplacementNamed
¶
pushReplacementNamed
is used to push a new route onto the navigator stack and simultaneously remove the current route. This means it replaces the current screen, so the user cannot navigate back to the previous screen.
Use Case: When you want to navigate to a new screen and do not want the user to navigate back to the previous screen. This is useful for workflows where the previous screen is no longer relevant, like after a successful login.
Comparison¶
-
Stack Behavior:
pushNamed
: Adds the new screen to the navigation stackpushReplacementNamed
: Replaces the current screen in the navigation stack with the new screen
-
Back Navigation:
pushNamed
: User can navigate back to the previous screenpushReplacementNamed
: User cannot navigate back to the previous screen and normally you will add adrawer
-
Use Cases:
pushNamed
: When you want to provide back navigation (e.g., navigating from a list of items to a detail screen)pushReplacementNamed
: When the previous screen is no longer relevant and you do not want to provide back navigation (e.g., after a login or splash screen)
What is the diff between onChanged
and onSaved
in DropdownButtonFormField?¶
tl;dr¶
-
onChanged
:- Called whenever the user changes the field.
- Ideal for immediate responses to user input.
-
onSaved
:- Called when the form is saved.
- Ideal for collecting final input values during form submission.
Example code:
DropdownButtonFormField<String>(
value: _selectedValue,
items: _dropdownItems,
onChanged: (String? newValue) {
setState(() {
_selectedValue = newValue!;
});
},
onSaved: (String? newValue) {
_savedValue = newValue!;
},
),
In Flutter, both onChanged
and onSaved
are callbacks used with form fields like DropdownButtonFormField
. They serve different purposes:
onChanged
¶
- Purpose: onChanged is called whenever the user selects a new value from the dropdown list.
- Execution Time: It is triggered immediately when the user changes the value.
- Use Case: Use onChanged when you need to respond to user input immediately. For example, if you want to update other parts of the UI or validate input as soon as the user makes a selection.
onSaved
¶
- Purpose: onSaved is called when the form is saved (usually when the form's save method is called).
- Execution Time: It is triggered only once, typically when the form is submitted using methods like formKey.currentState!.save().
- Use Case: Use onSaved when you want to collect the final value of the form field and do something with it, such as sending it to a server or saving it in a database.
What does parsing means?¶
Parsing is the process of converting data from one format to another, making it usable or understandable for further processing. This often involves reading a string or a stream of data and transforming it into a structured format that a program can work with.
Key Points About Parsing¶
- Conversion: Converts data from one format (often text) into another (such as integers, doubles, objects, etc.).
- Validation: Ensures the data meets the required format or structure.
- Extraction: Involves extracting specific pieces of information from a larger dataset.
Parsing in Python¶
In Python, parsing a string to an integer is straightforward:
Example: String to Integer in Python¶
Parsing in Dart¶
In Dart, parsing is done using similar methods, but the syntax is different.
Example 1: String to Integer¶
Example 2: String to Double¶
void main() {
String str = "123.45";
double number = double.parse(str);
print(number); // Output: 123.45
}
Wat does exceptions means?¶
What is an Exception?¶
An exception is an event that occurs during the execution of a program that disrupts the normal flow of instructions. It's a way to handle errors or other exceptional events that occur during runtime.
Why Use Exceptions?¶
Exceptions allow a program to continue running or to gracefully handle errors without crashing. Instead of stopping the entire program, exceptions provide a way to catch and respond to error conditions.
Basic Explanation¶
Imagine you're following a recipe to bake a cake. An exception is like discovering you're out of flour halfway through. Instead of giving up, you pause, handle the situation (maybe by borrowing flour from a neighbor), and then continue with the recipe.
Exceptions in Dart¶
In Dart, exceptions are handled using try, catch, and finally blocks. Here's a simple breakdown:
try
: The code that might throw an exception is placed inside a try blockcatch
: If an exception occurs, the catch block is executedfinally
: Code inside the finally block is executed regardless of whether an exception occurred
Example 1: Basic Exception Handling¶
Example of trying to parse a string to an integer:
void main() {
String input = "abc"; // This is not a valid integer
try {
int value = int.parse(input);
print("The value is $value");
} catch (e) {
print("An error occurred: $e");
} finally {
print("Parsing attempt finished.");
}
}
- The
try
block attempts to parse the string - If the parsing fails, the
catch
block handles the error - The
finally
block runs regardless of whether an error occurred or not
Example 2: Using tryParse for Safe Parsing¶
A safer way to handle parsing is using the tryParse method, which doesn't throw an exception but returns null if parsing fails:
void main() {
String input = "abc";
int? value = int.tryParse(input);
if (value == null) {
print("Failed to parse the input.");
} else {
print("The value is $value");
}
}
tryParse
returns null instead of throwing an exception.- You can check if the result is
null
and handle it accordingly.
What is Dependency Injection?¶
Dependency Injection (DI) is a design pattern used in software development to achieve Inversion of Control (IoC). In simpler terms, it's a way to provide a class with the objects it needs (its dependencies) from the outside rather than creating them itself. This promotes modularity and makes the code easier to maintain and test.
Key Concepts¶
- Dependency: An object that a class needs to function.
- Injection: The process of passing these dependencies to the class from the outside.
Layman Term Explanation¶
Imagine you own a coffee shop, and you need a coffee machine that requires water, coffee beans, and electricity to make coffee.
Without Dependency Injection¶
-
You (the coffee machine) have to buy your own water, coffee beans, and get connected to electricity:
CoffeeMachine: - Water: Buy your own water - CoffeeBeans: Buy your own coffee beans - Electricity: Connect to power by yourself
This means the coffee machine has to handle everything on its own, which makes it difficult to change any of its components without modifying the coffee machine itself.
With Dependency Injection¶
-
Someone else provides the water, coffee beans, and connects you to electricity:
CoffeeMachine: - Water: Provided by someone else - CoffeeBeans: Provided by someone else - Electricity: Connected by someone else
This way, the coffee machine only focuses on making coffee, while someone else takes care of providing what it needs. If you want to change the brand of coffee beans, you just provide a different coffee bean supplier.
What is the diff between ListView.builder
and ListView.separated
?¶
Overview¶
Flutter provides several ways to create scrollable lists, among them are ListView.builder
and ListView.separated
. Both are used to create dynamic, lazily loaded lists, but they serve slightly different purposes with key functional differences.
ListView.builder
¶
ListView.builder
is used to create a scrollable, linear array of widgets that are created on demand. This is particularly useful when dealing with large collections of data as it ensures that only visible items are built and maintained in memory.
Syntax¶
Key Parameters¶
- itemCount: The total number of items in the list.
- itemBuilder: A function that returns a widget to build for each item in the list, given its index.
Example¶
ListView.builder(
itemCount: 100,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text('Item $index'),
);
},
)
Use Case¶
Use ListView.builder
when you need a simple, efficient way to display a large number of widgets, and you don't need any custom separators between the items.
ListView.separated
¶
ListView.separated
is similar to ListView.builder
but adds the ability to include a separator widget between the items. This is useful for adding visual separation between list items such as dividers or spaces.
Syntax¶
ListView.separated(
itemCount: int,
itemBuilder: BuildContext context, int index,
separatorBuilder: BuildContext context, int index,
)
Key Parameters¶
- itemCount: The total number of items in the list.
- itemBuilder: A function that returns a widget to build for each item in the list, given its index.
- separatorBuilder: A function that returns a widget to build for separating each pair of items, given its index.
Example¶
ListView.separated(
itemCount: 100,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text('Item $index'),
);
},
separatorBuilder: (BuildContext context, int index) {
return Divider();
},
)
Use Case¶
Use ListView.separated
when you need to insert a separator widget (such as a Divider
or a custom widget) between your list items.
Key Differences¶
- Separator:
ListView.builder
: Does not include separators between items.-
ListView.separated
: Allows for a custom separator widget between items. -
Flexibility:
ListView.builder
: Simplified interface when separators are not needed.-
ListView.separated
: Additional flexibility for adding custom separators. -
Use Case:
ListView.builder
: Ideal for lists without separators or with more complex item rendering logic.ListView.separated
: Ideal for lists requiring visual separation between items, such as dividers or custom widgets.
Summary¶
ListView.builder
:- Purpose: Efficiently builds a scrollable list of widgets as they are scrolled into view.
- Syntax:
- Key Features: No separators between items.
-
Use Case: When you need a simple list without separators.
-
ListView.separated
: - Purpose: Builds a scrollable list of widgets with separators between each item.
- Syntax:
- Key Features: Allows for custom separators between items.
- Use Case: When you need visual separation between list items, such as dividers.
Both ListView.builder
and ListView.separated
are powerful tools in Flutter for creating efficient, scrollable lists, with ListView.separated
providing additional functionality for inserting separators between list items.