Practices to Simplify Flutter App Development in 2023

Pairroxz Technologies
7 min readNov 25, 2022

--

Credits:Pairroxz Technologies

One of Google’s most well-liked cross-platform mobile frameworks is Flutter. Since the framework is widely used by developers worldwide, there is a continual process of upgraded versions of Flutter, the latest of which is Flutter 3. The appropriate procedures for Flutter app development will be discussed today; using this blog as a reference will make the process of creating an app with Flutter simpler.

Best Practices for Developing Flutter Apps

The standard and simple procedures for Flutter developers to increase code quality, readability, maintainability, and productivity are discussed in this article.

Let’s get started:

1. Make the Build Function Pure:

The development method was designed so that it must be completely free of extraneous material. This is so that a new widget development can be initiated by a variety of external circumstances, some of which are listed below:

  • Pop/push route
  • Screen resizing, typically due to changes in the keyboard’s appearance or orientation
  • The parent widget recreated its kid.
  • An inherited widget depends on a class or context pattern change.

Avoid

@override

Widget build(BuildContext context) {

return FutureBuilder(

future: httpCall(),

builder: (context, snapshot) {

// create some layout here

},

);

}

Should be like this:

class Example extends StatefulWidget {

@override

_ExampleState createState() => _ExampleState();

}

class _ExampleState extends State<Example> {

Future<int> future;

@override

void initState() {

future = repository.httpCall();

super.initState();

}

@override

Widget build(BuildContext context) {

return FutureBuilder(

future: future,

builder: (context, snapshot) {

// create some layout here

},

);

}

}

2. Understanding the Flutter Idea of Limitations:

While providing Flutter App Development Services, every Flutter app developer should know the golden rule: limitations go down, sizes go up, and the parent sets the position. Let’s learn more about this in detail:

A widget is subject to limitations imposed by its parent. Four doubles are known as a constraint: a maximum and minimum width and maximum and minimum height.

The widget then checks each kid on its list. One by one, the device asks each of its children what size they want to be before giving each child a command that specifies the limits (which may vary for each kid).

After that, the widget places each of its children in turn (vertically on the y-axis and horizontally on the x-axis). The device then informs its parent of its size.

All widgets in Flutter give themselves as per their parent’s or their box limitations. But there are some restrictions to this.

For instance, you may choose the size of a child widget inside a parent widget. The device itself is unable to have any size. The widget’s size should not exceed the limitations imposed by its parent.

3. Using Operators Wisely to Cut Down on the Amount of Lines that Must be Executed:

  • Use Cascades Operator

The Cascades(..) operator should be used if we need to perform a series of operations on the same object.

//Do

var path = Path()

..lineTo(0, size.height)

..lineTo(size.width, size.height)

..lineTo(size.width, 0)

..close();

//Do not

var path = Path();

path.lineTo(0, size.height);

path.lineTo(size.width, size.height);

path.lineTo(size.width, 0);

path.close();

  • Use spread collections

Spread collection syntax leads to more straightforward and accessible code; therefore, it can be used when existing elements are already kept in another collection.

//Do

var y = [4,5,6];

var x = [1,2,…y];

//Do not

var y = [4,5,6];

var x = [1,2];

x.addAll(y);

  • Use the operators null aware (?.) and null safe (??)

Avoid using null tests in conditional expressions; instead, use the?? (if null) and?. (null aware) operators.

//Do

v = a ?? b;

//Do not

v = a == null ? b : a;

//Do

v = a?.b;

//Do not

v = a == null ? null : a.b;

  • Don’t use the “as” operator Utilize the “is” operator in its place

The cast operator often throws an exception if the cast cannot be made. Use ‘is’ to keep an exception from being thrown.

//Do

if (item is Animal)

item.name = ‘Lion’;

//Do not

(item as Animal).name = ‘Lion’;

4. Use Streams Only When Necessary:

Even while streams are quite powerful, if we use them, it places a great deal of responsibility on our shoulders to use this resource effectively.

The use of Streams can enhance memory and CPU use when they are performed poorly. Not only that but failing to shut the streams will result in memory leaks.

Therefore, in these situations, you can use something else that uses less memory, such as ChangeNotifier for reactive UI, instead of Streams. We can choose the Bloc library for more sophisticated features because it focuses more on resource efficiency and provides a straightforward interface for creating reactive user interfaces.

As long as streams aren’t used, they will be effectively cleaned. The concern is that simply removing the variable won’t guarantee that it isn’t used. It could continue to function in the background.

To ensure that the linked StreamController is stopped and that the GC can later release resources, you must execute Sink. close().

Use the StatefulWidget.dispose of function to accomplish that:

abstract class MyBloc {

Sink foo;

Sink bar;

}

class MyWiget extends StatefulWidget {

@override

_MyWigetState createState() => _MyWigetState();

}

class _MyWigetState extends State<MyWiget> {

MyBloc bloc;

@override

void dispose() {

bloc.bar.close();

bloc.foo.close();

super.dispose();

}

@override

Widget build(BuildContext context) {

// …

}

}

5. Develop Tests for Essential Functionality:

The risks of depending on manual testing will always exist, but having an automated set of tests can help you save a sizable amount of time and work. Since Flutter primarily targets various platforms, testing every capability after every change would be time-consuming and require repeated effort.

Having 100% code coverage for testing will be the most excellent option, but depending on the amount of time and money available, it might not always be possible. However, it’s still crucial to have tests that at least cover the app’s main features.

The best starting points are unit and widget tests because they are less time-consuming than integration tests.

6. Use raw string:

To avoid finding only backslashes and dollars as escaping, use a raw string.

//Do

var s = r’This is demo string \ and $’;

//Do not

var s = ‘This is demo string \\ and \$’;

7. Switch to Relative Imports from Absolute Imports:

When utilizing relative and absolute imports together, it is easy to confuse when the same class is imported in two different methods. We should adopt a relative path in the lib/ subdirectory to prevent this scenario.

//Do

import ‘../../themes/style.dart’;

//Do not

import ‘package:myapp/themes/style.dart’;

8. Switching to SizedBox in Flutter Instead of Container:

There are several situations where using a placeholder is necessary.

Here is the perfect illustration:

return _isNotLoaded ? Container() : YourAppropriateWidget();

In Flutter, you’ll use the Container a lot. It’s a terrific widget. Container() is not a const function Object() { [native code] }; instead, it grows up to fit the limitations set by the parent.

The SizedBox, on the other hand, is a const function Object() { [native code] } that creates a fixed-size box. The width and height parameters can be set to null to indicate that the box’s size should not be constrained in any direction.

So, instead of using a container to implement the placeholder, we should use SizedBox.

return _isNotLoaded ? SizedBox() : YourAppropriateWidget();

9. Use Log Instead Print:

Print() and Login() are always used when accessing the console. Android occasionally throws away several log lines if you use print() and obtain too much output at once.

Use debugPrint to prevent this from happening again (). Use Dart: Developer Log if your log data contains more than enough information ().

//Do

log(‘data: $data’);

//Do not

print(‘data: $data’);

  • Utilize the ternary operator in single-line instances.

String alert = isReturningCustomer ? ‘Welcome back to our site!’ : ‘Welcome, please sign up.’;

  • Use the if statement rather than the ternary operator in the following scenario.

Widget getText(BuildContext context) {

return Row(

children:

[

Text(“Hello”),

if (Platform.isAndroid) Text(“Android”) (here if you use ternary then that is wrong)

]

);

}

  • Whenever possible, utilize const widgets. We should specify the device as a constant, so it does not change when setState is called. The widget’s ability to be rebuilt to boost efficiency will be hindered.

//Do

const SizedBox(height: Dimens.space_normal)

//Do not

SizedBox(height: Dimens.space_normal)

10. Avoid Explicitly Initializing Variables to Null:

When a variable’s value is not supplied in Dart, the variable is automatically initialized to null; thus, including null is unnecessary.

//Do

int _item;

//Do not

int _item = null;

When the value type of a member is known, it should always be highlighted. Never use var if it is not necessary. Var takes extra time and space to resolve because it is dynamic.

//Do

int item = 10;

final Car bar = Car();

String name = ‘john’;

const int timeOut = 20;

//Do not

var item = 10;

final car = Car();

const timeOut = 2000;

11. Whenever Feasible, Use the Const Keyword:

Trash collectors’ workload can be minimized by giving widgets a const function Object() { [native code] }. When the app is large enough or if there is a frequently rebuilt view, this may appear to be a minor performance issue, but it adds up and matters.

Const declarations are additionally better for hot reloading. Furthermore, we should disregard the superfluous const keyword. Look at the code below:

const Container(

width: 100,

child: const Text(‘Hello World’)

);

Although const is already applied on the parent widget, we do not need to use it for the Text widget.

Dart offers the next Linter rules for const:

prefer_const_constructors

prefer_const_declarations

prefer_const_literals_to_create_immutables

Unnecessary_const

Conclusion

The industry for developing apps is currently rife with competition. You must make your app distinctive if you want it to stand out from the competition. Make it remarkable and powerful enough to capture the user’s interest. Flutter can be an excellent choice if you want to turn your fledgling app idea into a profitable one. It enables the building of user-friendly apps that work flawlessly on various platforms. In contrast to many other platforms, Flutter Mobile App Development Company allows app developers to easily construct intuitive apps with dynamic UI for iOS and Android device users.

--

--

Pairroxz Technologies

Pairroxz was founded in 2011 with the aim of providing the best e-solutions to people (clients) and simplifying their goals through app development.