Movatterモバイル変換


[0]ホーム

URL:


Packt
Search iconClose icon
Search icon CANCEL
Subscription
0
Cart icon
Your Cart(0 item)
Close icon
You have no products in your basket yet
Save more on your purchases!discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Profile icon
Account
Close icon

Change country

Modal Close icon
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timerSALE ENDS IN
0Days
:
00Hours
:
00Minutes
:
00Seconds
Home> Mobile> Cross Platform Mobile Development> Flutter Design Patterns and Best Practices
Flutter Design Patterns and Best Practices
Flutter Design Patterns and Best Practices

Flutter Design Patterns and Best Practices: Build scalable, maintainable, and production-ready apps using effective architectural principles

Arrow left icon
Profile Icon Daria OrlovaProfile Icon Esra KadahProfile Icon Jaime Blasco
Arrow right icon
$9.99$35.99
Full star iconFull star iconFull star iconFull star iconFull star icon5(4 Ratings)
eBookSep 2024362 pages1st Edition
eBook
$9.99 $35.99
Paperback
$44.99
Subscription
Free Trial
Renews at $19.99p/m
Arrow left icon
Profile Icon Daria OrlovaProfile Icon Esra KadahProfile Icon Jaime Blasco
Arrow right icon
$9.99$35.99
Full star iconFull star iconFull star iconFull star iconFull star icon5(4 Ratings)
eBookSep 2024362 pages1st Edition
eBook
$9.99 $35.99
Paperback
$44.99
Subscription
Free Trial
Renews at $19.99p/m
eBook
$9.99 $35.99
Paperback
$44.99
Subscription
Free Trial
Renews at $19.99p/m

What do you get with eBook?

Product feature iconInstant access to your Digital eBook purchase
Product feature icon Download this book inEPUB andPDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature iconDRM FREE - Read whenever, wherever and however you want
Product feature iconAI Assistant (beta) to help accelerate your learning

Contact Details

Modal Close icon
Payment Processing...
tickCompleted

Billing Address

Table of content iconView table of contentsPreview book icon Preview Book

Flutter Design Patterns and Best Practices

Best Practices for Building UIs with Flutter

Flutter is rapidly becoming a go-to framework for creating applications of various scales. Google Trends and Stack Overflow confirm that Flutter has become a more popular search term than React Native for the last several years (seehttps://trends.google.com/trends/explore?q=%2Fg%2F11f03_rzbg,%2Fg%2F11h03gfxy9&hl=en andhttps://insights.stackoverflow.com/trends?tags=flutter%2Creact-native). Flutter consistently appears in various development ratings: the top 3 GitHub repositories by number of contributors (https://octoverse.github.com/2022/state-of-open-source), the top 3 most downloaded plugins in JetBrains Marketplace (https://blog.jetbrains.com/platform/2024/01/jetbrains-marketplace-highlights-of-2023-major-updates-community-news/), and second in Google Play Store ratings right afterKotlin (https://appfigures.com/top-sdks/development/apps).

This isn’t a surprise, since Flutter offers a delightful toolkit that allows developers to build smooth and pixel-perfect UIs almost immediately after their first encounter with the framework. Flutter also does a great job of hiding away the details of the rendering process. However, because it is so easy to overlook those details, a lack of understanding of how the framework actually works can lead toperformance issues.

This chapter explores the benefits of using Flutter’s declarative UI-building approach, as well as how that approach affects developers. We will discuss methods for optimizing performance and avoiding interference with the framework’s building and rendering processes. We will also examine how this approach works under the hood and provide best practices for creating beautiful andblazing-fast interfaces.

By the end of this chapter, you will understand the concept of the Flutter tree system and how to scope your widget tree for the best performance. This knowledge will provide the foundation necessary for learning architectural design patterns based on the framework’sbuild system.

In this chapter, we’re going to cover the followingmain topics:

  • Understanding the difference between declarative and imperativeUI design.
  • Everything is a widget! Oris it?
  • Reduce,reuse, recycle!

Understanding the difference between declarative and imperative UI design

The beauty of technology is that it evolves with time based on feedback about developer experience. Today, if you’re in mobile development, there is a high chance that you have heard about Jetpack Compose, SwiftUI, React Native, and of course Flutter. The thing these technologies have in common is both that they’re used for creating mobile applications and the fact that they do it via a declarative programming approach. You may have heard this term before, but what does it actually mean and why isit important?

To take full advantage of a framework, it’s important to understand its paradigm and work with it rather than against it. Understanding the “why” behind the architectural decisions makes it much easier to understand the “how,” and to apply design patterns that complement theoverall system.

Native mobile platforms have a long history of development and major transitions. In 2014, Apple announced a new language, Swift, that would replace the current Objective-C. In 2017 the Android team made Kotlin the official language for Android development, which would gradually replace Java. Those introductions had a hugely positive impact on the developer experience, yet they still had to embrace the legacy of existing framework patterns and architecture. In 2019, Google announced Jetpack Compose and Apple announced SwiftUI – completely new toolkits for building UIs. Both SwiftUI and Jetpack Compose take advantage of their respective languages, Swift and Kotlin, leaving legacy approaches behind. Both toolkits also loudly boast their declarative programming paradigm. But language advantages aside, let’s explore why declarative is now the industrial de facto and what is wrongwith imperative.

Understanding the imperative paradigm

By definition, the imperative programming paradigm focuses on how to achieve the desired result. You describe the process step by step and have complete control of the process. For example, it could result in code suchas this:

fun setErrorState(errorText: String) {    val textView = findViewById<TextView>(R.id.error_text_view)    textView.text = errorText    textView.setTextColor(Color.RED)    textView.visibility = View.VISIBLE    val button = findViewById<Button>(R.id.submit_button)    button.isEnabled = true    val progressView = findViewById<ProgressBar>(R.id.progress_view)    progressView.visibility = View.GONE}

In the preceding snippet, we imperatively described how to update the UI in case of an error. We accessed the UI elements step by step and mutatedtheir fields.

This is a real example of code that could’ve been written for a native Android application. Even though this approach may be powerful and gives the developer fine-grained control over the flow of the logic, it comes with the possibility of thefollowing problems:

  • The more elements that can change their presentation based on a state change, the more mutations you need to handle. You can easily imagine how this simplesetErrorState becomes cumbersome as more fields need to be hidden or changed. The approach also assumes that there are similar methods for handling a progress and success state. Code such as this may easily become hard to manage, especially as the amount of views in your app grows and the state becomesmore complex.
  • Modifying the global state can produce side effects. On every such change, we mutate the same UI element and possibly call other methods that also mutate the same elements. The resulting myriad of nested conditionals can quickly lead to inconsistency and illegal states in the final view that the user sees. Such bugs tend to manifest only when certain conditions are met, which makes them even harder to reproduceand debug.

For many years, the imperative approach was the only way to go. Thankfully, native mobile frameworks have since started adopting declarative toolkits. Although these are great, developers who need to switch between paradigms inside of one project can encounter many challenges. Different tools require different skills and in order to be productive, the developer needs to be experienced with both. More attention needs to be paid to make sure that the application that is created with various approaches is consistent. While the new toolkits are in the process of wider adoption, some time and effort are required until they are able to fully implement what their predecessors already have. Thankfully, Flutter embraced declarative fromthe start.

Understanding the declarative paradigm

In an imperative approach, the focus is on the “how.” However, in the declarative approach, the focus is on the “what.” The developer describes the desired outcome, and the framework takes careof the implementation details. Since the details are abstracted by the framework, the developer has less control and has to conform to more rules. Yet the benefit of this is the elimination of the problems imposed by the imperative approach, such as excessive code and possible side effects. Let’s take a look at thefollowing example:

Widget build(BuildContext context) {     final isError = false;     final isProgress = true;     return Column(      children: [        MyContentView(          showError: isError,        ),        Visibility(          visible: isProgress,          child: Center(            child: CircularProgressIndicator(),          ),        ),      ],    );}

In the preceding code, we have built a UI as a reaction to state changes (such as theisError orisProgress fields). In the upcoming chapters, you will learn how to elegantly handle the state, but for now, you only need to understandthe concept.

This approach can also be called reactive, since the widget tree updates itself as a reaction to a changeof state.

Does Flutter use the declarative or imperative paradigm?

It is important to understand that Flutter is a complex framework. Conforming to just one programming paradigm wouldn’t be practical, since it would make a lot of things harder (seehttps://docs.flutter.dev/resources/faq#what-programming-paradigm-does-flutters-framework-use). For example, a purely declarative approach with its naturalnesting of code would, make describing aContainer orChip widget unreadable. It would also make it more complicated to manage all oftheir states.

Here’s an excerpt from thebuild method of theContainer describing how to build the childwidget imperatively:

  @override  Widget build(BuildContext context) {    Widget? current = child;    // ...    if (color != null) {      current = ColoredBox(color: color!, child: current);    }    if (margin != null) {      current = Padding(padding: margin!, child: current);    }    // ...}

Even though the main approach of describing the widget tree can be viewed as declarative, imperative programmingcan be used when it feels less awkward to do so. This is why understanding the concepts, patterns, andparadigms is crucial to creating the most efficient, maintainable, andscalable solutions.

If you are coming from an imperative background, getting used to the declarative approach of building the UI may be mind-bending at first. However, shifting your focus from “how” to “what” you’re trying to build will help. Flutter can help you too, as instead of mutating each part of the UI separately, Flutter rebuilds the entire widget tree as a reaction to state changes. Yet the framework stillmaintains snappy performance, and developers usually don’t need to think aboutit much.

In the next section, let’s take a closer look at the abstraction to understandhow thewhat actually works. We will explore not only how to use the widgets as a developer but also how the framework efficiently handles them under the hood. We will cover what to do and what not to do to avoid interfering with the building andrendering processes.

Everything is a widget! Or is it?

You have probably heard this phrase many times. It has become the slogan of Flutter – in Flutter, everything is a widget! But what is a widget and how true is this saying? At first glance, the answer might seem simple: a widget is a basic building block of UI, and everything you see on the screen is a widget. While this is true, these statements don’t provide much insight into the internals ofa widget.

The framework does a good job of abstracting those details away from the developer. However, as your app grows in size and complexity, if you don’t follow best performance practices, you may start encountering issues related to frame drop. Before this can happen, let’s learn about the Flutter build system and how to make the mostof it.

What is a widget?

For most of our development, wewill create widgets that extendStatelessWidget orStatefulWidget. The following is the codefor these:

abstract class StatelessWidget extends Widget {...}abstract class StatefulWidget extends Widget {...}@immutableabstract class Widget {...}

From the source code, we can see that both of these widgets are abstract classes and that they inherit from the same class: theWidget class.

Another important place where we see theWidget class is in ourbuild method:

Widget build(BuildContext context) {...}

This is probably the most overridden method in a Flutter application. We override it every time we create a new widget and we know that this is the method that gets called to render the UI. But how often is this method called? First of all, it can be called whenever the UI needs an update, either by the developer, for example, viasetState, or by the framework, for example, on an animation ticker. Ultimately, it can be called as many times as your device can render frames in a second, which is represented by the refresh rate of your device. It usually ranges from 60 Hz to 120 Hz. This means that thebuild method can be called 60-120 times per second, which gives it 16-8 ms (1,000 ms / 60 frames-per-second = 16 ms or 1, 000 ms / 120 frames-per-second = 8 ms) to render the wholebuild method of your app. If you fail to do that, this will result in a frame drop, which might mean UI jank for the user. Usually, this doesn’t make users happy! But all developer performance optimizations aside, surely this can’t be what’s happening? Redrawing the whole application widget tree on every frame would certainly impact performance. This is not what happens in reality, so let’s find out how Flutter solvesthis problem.

When we look at theWidget class signature, we see that it is marked with an@immutable annotation. From a programming perspective, this means that all of the fields of this class have to befinal. So after you create an instance of this class, you can’t mutate any of its fields (collections are different but let’s ignore this for now and return to it inChapter 4). This is an interesting fact when you remember that the return type of ourbuild method isWidget and that this method can be called up to 120 times per second. Does that mean that every time we call thebuild method, we will return a completely new tree of widgets? All million of them? Well, yes and no. Depending on how you build your widget tree and why and where it was updated, either the whole tree or only parts of it get rebuilt. But widgets are cheap to build. They barely have any logic and mostly serve as a data class for another Flutter tree that we will soon observe. Before we move on to this tree though, let’s take a look at one specialtypeof widget.

Getting to know the RenderObjectWidget and its children

We have already discussed that when dealing with widgets, we mostly extendStatelessWidget andStatefulWidget. Inside thebuild method of our widgets, we only compose them like Lego bricks using the widgets already provided by the Flutter framework, such asContainer andTextFormField, or ourown widgets.

Most of the time, we only use thebuild method. Less often, we may use other methods such asdidChangeDependencies ordidUpdateWidget from theState object. Sometimes we may use our own methods, such as click handlers. This is the beauty of a declarative UI toolkit: we don’t even need to know how the UI we compose is actually rendered. We just use the API. However, in order to understand the intricacies of the Flutter build system, let’s think about it fora moment.

How many times have you usedSizedBox to add some spacing between other widgets? An interesting thing about this widget is that it extends neitherStatelessWidget norStatefulWidget. It extendsRenderObjectWidget. As a developer, you will rarely need to extend this widget or any other that containsRenderObjectWidget in its title. The important thing to know about this widget is that it is responsible for rendering, as the name suggests. Each child ofRenderObjectWidget has an associatedRenderObject field. TheRenderObject class is one of the three pillars of the Flutter build system (the first being the widget and the last being theElement, which we will see in the next section). This is the class thatdeals with actual low-level rendering details, such as translating user intentions ontothe canvas.

Let’s take a look at another example: theText widget. Here is a piece of code for a very simple Flutter app that renders theHello, Flutter text onthe screen:

void main() {  runApp(    const MaterialApp(      home: Text('Hello, Flutter'),    ),  );}

We use two widgets here: theMaterialApp, which extendsStatefulWidget, andText, which extends aStatelessWidget. However, if we take a deeper look inside theText widget, we will see that from itsbuild method, aRichText widgetis returned:

// Some code has been omitted for demo purposesclass Text extends StatelessWidget {  @override  Widget build(BuildContext context) {    return RichText(...);  }}class RichText extends MultiChildRenderObjectWidget {...}

An important difference here is thatRichText extendsMultiChildRenderObjectWidget, which is just a subtype of aRenderObjectWidget. So even though we didn’t do it explicitly, the last widget in our widget tree extendsRenderObjectWidget. We can visualize the widget tree, and it will look somethinglike this:

Figure 1.1 – Visual example of a widget tree

Figure 1.1 – Visual example of a widget tree

Even though you, as a developer, won’t be extendingRenderObjectWidget often, you need to rememberone takeaway!

This is important!

No matter how aggressively you compose your widget tree, the widgets that are actually responsible for the rendering will always extend theRenderObjectWidget class. Even if you, as a developer, don’t do it explicitly, you should know that this is what is happening deeper in the widget tree. You can always verify this by following the nesting of thebuild methods.

Let’s sum up whatwe’ve learned about thewidget types:

StatelessWidgetand StatefulWidget

RenderObjectWidget

Function

Composing widgets

Renderingrender objects

Methods

build

createRenderObject

updateRenderObject

Extendedby developer

Often

Rarely

Examples

Container,Text

SizedBox,Column

Table 1.1 – Widget differences

But if widgets are immutable, then who updates therender objects?

Unveiling the Element class

From thecreateRenderObject andupdateRenderObject we understand that renderobjects are mutable. Yet the widgets themselves that create those render objects are immutable. So how can they update anything, if they are recreated every time theirbuild methodis called?

The secret lies within theWidget API itself. Let’s take a closer look at some of its methods, startingwithcreateElement:

@immutableabstract class Widget {  Element createElement();}

The first method that should interest us iscreateElement, which returns anElement. The element is the last of the three pillars of the Flutter build system. It does all of the shadow work, giving the spotlight to the widget.createElement gets called the first time the widget is added to the widget tree. The method calls the constructor of the overridingElement, such asStatelessElement. Let’s take a look at what happens in the constructor of theElement class:

abstract class Element { Widget? _widget; Element(Widget widget)      :_widget = widget {...}}

We pass thewidget field as the parameter to the constructor and assign it to the local_widget field. This way, the Element retains the pointer to the underlying widget, yet the widget doesn’t retain the pointer to the element. The_widget field of the element is notfinal, which means that it can be reassigned. But when? The framework calls theupdate method of theElement any time the parent wishes to change the underlying widget. Let’s take a look inside theupdate methodsource code:

abstract class Element { void update(covariant Widget newWidget) {   _widget = newWidget; }}

As we can see, the pointer to the_widget field is changed tonewWidget, so the old widget is thrown away, yet our element stays the same. But in order for this reassignment to happen and for this method to be called, firstly thecanUpdate method of theWidget class is called. ThecanUpdate method checks whether theruntimeType andkey of the old and new widgets are the sameas follows:

abstract class Widget { static bool canUpdate(Widget oldWidget, Widget newWidget) {     return oldWidget.runtimeType == newWidget.runtimeType         && oldWidget.key == newWidget.key;   }}

Only if this method returnstrue, which means that theruntimeType andkey of the old and new widgets are the same, can we update our element with a new widget. Otherwise, the whole subtree will be disregarded and a completely new element will be inserted inthis place.

To better understand the flow of this process, let’s take a look at thefollowing diagram:

Figure 1.2 – Element relationship with the widget

Figure 1.2 – Element relationship with the widget

The fact that the element can be updated instead of being recreated is even more important for the performance ofRenderObjectWidget, since it deals with render objects that do the low-level painting. In the update method ofRenderObjectElement, wealso callupdateRenderObject, which is a performance-optimized method: it only updates the render objects if there are any changes, and it only updates them partially. That’s why even though there may be many calls of the build method, it doesn’t mean that the whole tree getscompletely repainted.

Finally, let’s summarize everything we’ve learned about the Fluttertree system:

Widget

Element

RenderObject

Mutable?

No

Yes

Yes

Cheapto create?

Yes

No

No

Created onevery build?

Yes

No

No

Usedby devs

Always

Almost never

Very rarely

Relationships

Every widget has anElement, but not every widget hasaRenderObject

ImplementsBuildContext and has access both to the widget, and theRenderObject (ifit exists)

Only created by implementers ofRenderObjectWidget

Table 1.2 – Summary of widget, Element, and RenderObject roles

As we have just seen, Flutter has some straightforward yet elegant algorithms that make sure your application runs smoothly and looks flawless. Unfortunately, it doesn’t mean that the developer doesn’t have to think about performance at all, since there are many ways the performance can be impacted negatively if the best practices are ignored. Let’s take a look at how we can support Flutter in maintaining a delightful experience forour users.

Reduce, reuse, recycle!

Now that we know that thebuild method can be called up to 120 times per second, the question is: do we really need to call thewholebuild method of our app if only a small part of the widget tree has changed? The answer is no, of course not. So let’s review how we can makethis happen.

First things first, let’s get one obvious but still important thing out of the way. Thebuild method is supposed to be blazing fast. After all, it can have as little as 8 ms to run without dropping frames. This is why it’s crucial to keep any long-running tasks such as network or database requests out of this method. There are better places to do that which we will explore in detail throughoutthis book.

Pushing rebuilds down the tree

There can be several situationswhen pushing the rebuilds down the tree can impact performance in apositive way.

Calling setState of StatefulWidget

One of the most used widgets isStatefulWidget. It’s a very convenient type of widget because it can manage state changes and react to user interactions. Let’s take a look at the sample app that iscreated every time you start a new Flutter project: the counter app. We are interested in the code of the_MyHomePageState class, which is theStateofMyHomePage:

class _MyHomePageState extends State<MyHomePage> {  int _counter = 0;  void _incrementCounter() {    setState(() { _counter++; });  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Flutter Demo Home Page'),      ),      body: Center(        child: Column(          mainAxisAlignment: MainAxisAlignment.center,          children: <Widget>[            const Text(              'You have pushed the button this many times:',            ),            Text(              '$_counter',              style: Theme.of(context).textTheme.headlineMedium,            ),            // In the original Flutter code the increment button is a            // FloatingActionButton property of the Scaffold,            // but for demonstration purposes, we need a slightly             // modified version            TextButton(              onPressed: _incrementCounter,              child: const Text('Increase'),            )          ],        ),      ),    );  }}

The UI is very simple. It consists of aScaffold with anAppBar and aFloatingActionButton. Clicking theFloatingActionButton increments the internal_counter field. The body of theScaffold is aColumn with twoText widgets that describe howmany times theFloatingActionButton has been clicked based on the_counter field. The preceding example differs from the original Flutter sample in one regard: instead of using theFloatingActionButton for handling clicks, we are using theTextButton. So every time we click theTextButton, the_incrementCounter method is called, which in turn calls thesetState framework method and increments the_counter field. Under the hood, thesetState method causes Flutter to call thebuild method of_MyHomePageState, which causes a rebuild. An important thing here is thatsetState causes a rebuild of the wholeMyHomePage widget, even though we are only changingthe text.

An easy way to optimize this is topush state changes down the tree by extracting them into a smaller widget. For example, we can extract everything that was inside theCenter widget ofScaffold into a separate widget and callitCounterText:

class _MyHomePageState extends State<MyHomePage> {  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Flutter Demo Home Page'),      ),      body: const Center(child: CounterText()),    );  }}class CounterText extends StatefulWidget {  const CounterText({Key? key}) : super(key: key);  @override  State<CounterText> createState() => _CounterTextState();}class _CounterTextState extends State<CounterText> {  int _counter = 0;  void _incrementCounter() {    setState(() { _counter++; });  }  @override  Widget build(BuildContext context) {    return Column(...// Same code that was in the original example );  }}

We haven’t changed any logic. We only took the code that was inside of theCenter widget of_MyHomePageState and extracted it into a separate widget:CounterText. By encapsulating the widgets that need to be rebuilt when an internal field changes into a separate widget, we ensure that whenever we callsetState inside of the_CounterTextState field, only the widgets returned from thebuild method of_CounterTextState get rebuilt. The parent_MyHomePageState doesn’t get rebuilt, because itsbuild method wasn’t called. We pushed the state changes down the widget tree, causing only smaller parts of the tree to get rebuilt, instead of the whole screen. In real-life app code, this scales very fast, especially if your pagesare UI-heavy.

Subscribing to InheritedWidget changes via .of(context)

By extracting the changing counter text into a separateCounterText widget in the last code snippet, we have actually made one more optimization. The interesting line for us isTheme.of(context).textTheme.headlineMedium. You have certainly usedTheme and other widgets, such asMediaQuery orNavigator, via the.of(context) pattern. Usually, those widgets extend a special type of class:InheritedWidget. We will look deeper into its internals in the state management part (Chapters 3 and4), but for now, we are interested in two ofits properties:

  • Instead of creating thosewidgets, we will access them via static getter and use some of their properties. This means that they were created somewhere higher up the tree. Hence, we will inherit them. If they weren’t and we still try to look them up, we will getan error.
  • For some of those widgets, such asTheme andMediaQuery, the.of(context) not only returns the instance of the widget if it finds one but also adds the calling widget to a set of its subscribers. When anything in this widget changes – for example, if theTheme was light and became dark – it will notify all of its subscribers and cause them to rebuild. So in the same way as withsetState, if you subscribe to anInheritedWidget, changes high up in the tree will cause the rebuild of the whole widget tree starting from the widget that you have subscribed in. Push the subscription down to only those widgets that actuallyneed it.

Extra performance tip

You may have usedMediaQuery.of(context) in order to fetch information about the screen, such as its size, paddings, and view insets. Whenever you callMediaQuery.of(context), you subscribe to the wholeMediaQuery widget. If you want to get updates only about the paddings (or the size, or the view insets), you can subscribe to thisspecific property by callingMediaQuery.paddingOf(context),MediaQuery.sizeOf(context), and so on. This is becauseMediaQuery actually extends a specific type ofInheritedWidget – theInheritedModel widget. It allows you to subscribe only to those properties that you care about as opposed to the whole widget, which can greatly contribute to widgetrebuild optimization.

Avoiding redundant rebuilds

Now that we’ve learned how to scope our trees so that only smaller sections are rebuilt, let’s find out how to minimize the amount of thoserebuilds altogether.

Being mindful of the widget life cycle

Stateless widgets are boring in terms of their life cycles. Stateful widgets, on the other hand, are not. Let’s take a look at the life cycle oftheState:

Figure 1.3 – Main methods of State life cycle

Figure 1.3 – Main methods of State life cycle

Here are a few things that we shouldcare about:

  • TheinitState method gets called only once per widget life cycle, much like thedispose method.
  • ThedidChangeDependencies method gets called immediatelyafterinitState.
  • didChangeDependencies is always called when anInheritedWiget that we subscribed to has changed. This is the implementation aspect of what we have just discussed in theprevious section.
  • The build method always gets called afterdidChangeDependencies,didUpdateWidget,andsetState.

This is important!

Don’t callsetState indidChangeDependencies ordidUpdateWidget. Such calls are redundant, since the framework will always callbuild afterthose methods.

The best performance practices in the preceding list are also the reason why it’s better to decouple your widgets into other custom widgets rather than extract them into helper methods such asWidget buildMyWidget(). The widgets extracted into methods still access the same context or callsetState, which causes the whole encapsulating widget to rebuild, so it’s generally recommended to prefer widget classes ratherthan methods.

One more important thing regarding the life cycle of theState is that once itsdispose method has been called, it will never become alive again and we will never be able to use it again. This means that if we have acquired any resources that hold a reference to thisState, such as text editing controllers, listeners, or stream subscriptions, these should be released. Otherwise, the references to these resources won’t let the garbage collector clean up this object, which will lead to memory leaks. Fortunately, it’s usually very easy to release resources by calling their owndispose orclose methods inside thedispose oftheState.

Caching widgets implicitly

Dart has a notion of constant constructors. We can create constant instances of classes by adding aconst keyword before the class name. But when can we do this and how can we take advantage of themin Flutter?

First of all, in order to be able todeclare aconst constructor, all of the fields of the class must be marked asfinal and be known at compile time. Second, it means that if we create two objects viaconst constructors with the same params, such asconst SizedBox(height: 16), only one instance will be created. Aside from saving some memory due to initializing fewer objects, this also provides benefits when used in a Flutter widget tree. Let’s return to ourElement classonce again.

We remember that the class has anupdate method that gets called by the framework when the underlying widget has changed its fields (but not type or key). This method changes the reference to the widget. Soon the framework calls rebuild. Since we’re working with a tree data structure, we will traverse its children. Unless your element is a leaf element, it will have children. There is a very important method in theElement API calledupdateChild. As the name says, it updates its children elements. But the interesting thing is how itdoes it:

#1 Element? updateChild(Element? child, Widget? newWidget,    Object? newSlot) {#2    // A lot of code removed for demo purposes#3#4    final Element newChild;#5    if (child.widget == newWidget) {#6        newChild = child;#7     } else if (Widget.canUpdate(child.widget, newWidget)) {#8        child.update(newWidget);#9        newChild = child;#10    }#11#12    return newChild;#13 }

In the preceding code, in case our current widget is the same as the new widget as determined by the== operator, we only reassign the pointer, and that’s it. By default, in Dart, the== operator returnstrue only if both of the instances point to the same address in memory, which istrue if they were created via aconst constructor with thesame params.

However, if the result isfalse, weshould check the already-familiarWidget.canUpdate. However, aside from reassigning the pointer to the new element, we also call itsupdate method, which soon causesa rebuild.

Hence, if we useconst constructors, we can avoid rebuilds of whole widget subtrees. This is also sometimes referred to as caching widgets. So useconst constructors whenever possible and see whether you can extract your own widgets that can make use ofconst constructors, even if nestedwidgets can’t.

Keep in mind that you have to actually use theconst constructor, not just declare it as a possibility. For example, we have aConstText widget that has aconst constructor:

class ConstText extends StatelessWidget {  const ConstText({super.key});  @override  Widget build(BuildContext context) {    return const Text('Hello World');  }}

However, if we create an instance of this widget without using theconst constructor via theconst keyword as in the following code, then we won’t get any of the benefits of theconst constructor:

// Don't!class ParentWidget extends StatelessWidget {  const ParentWidget({Key? key}) : super(key: key);  @override  Widget build(BuildContext context) {    return ConstText(); // not const!  }}

We need to explicitly specify theconst keyword when creating an instance of the class. The correct usage of theconst constructor lookslike this:

// Doclass ParentWidget extends StatelessWidget {  const ParentWidget({Key? key}) : super(key: key);  @override  Widget build(BuildContext context) {    return const ConstText(); // const, all good  }}

In the preceding code, we used theconst keyword during the creation of aConstText widget. This way, we will get all of the benefits. This small keyword isvery important.

Explicitly cache widgets

The same logic can be applied if the widget can’t be created with aconst constructor, but can be assigned to afinal field of theState. Since you’re literally saving the pointer to the same widget instance and returning it rather than creating a new one, it will follow the same execution path as the one we saw withconst widgets. This is one of the ways in which you can work around theContainer not beingconst. You might do so using the following,forexample:

class _MyHomePageState extends State<MyHomePage> {  final greenContainer = Container(    color: Colors.green,    height: 100,    width: 100,  );  @override  Widget build(BuildContext context) {    return Column(      children: [        greenContainer,        Container(          color: Colors.pink,          height: 100,          width: 100,        ),      ],    );  }}

In the preceding code, theupdate method of the green container won’t be called. We have retained the reference to an already-existing widget by caching it in a localgreenContainer field. Hence, we return the exact same instance as in the previousbuild. This falls into the case described on line 5 in theupdateChild method code snippet providedearlier in this section. If the instances are the same based on the equality operator, then theupdate method is not called. On the other hand, the pinkContainer will be rebuilt every time because we create a new instance of the class every time thebuild method is called. This is described in line 7 of the samecode snippet.

Avoiding redundant repaints

Up to this point, we have looked at tips to help you avoid causing redundant rebuilds of the widget and element trees. The truth is that the building phase is quite cheap when compared to the rendering process, as this is where all of the heavy lifting is done. Flutter optimizes this phase as much as possible, but it cannot completely control how we create our interfaces. Therefore, we may encounter cases where these optimizations are not enough or are notworking effectively.

Let’s take a look at what happens when one of the render objects wants to repaint itself. We may assume that this repainting is scoped to that specific object – after all, it was the only one marked for repaint. But this is notwhat happens.

The thing is, even though we have scoped our widget tree, our render object tree has a relationship of its own. If a render object has been marked as needing repainting, it will not only repaint itself but also ask its parent to mark itself for repaint too. That parent then asks its parent, and so on, until the very root. And when it finally comes to painting, the object will also repaint all of its descendants. This happens until the framework encounters what is known as arepaint boundary. A repaint boundary is a Flutter way of saying “stop right here, there is nothing further to repaint.” This is done by wrapping your widget into another widget – yes, theRepaintBoundary widget.

If we wanted to depict this flow visually, it would be somethinglike this:

Figure 1.4 – Flow of the render object’s repainting process

Figure 1.4 – Flow of the render object’s repainting process

Here is what’s happening inFigure 1.4:

  1. We start fromRenderObject #12, which was the initial one to be markedfor repainting.
  2. The object goes on to callparent.markNeedsPaint ofRenderObject #10. Since theisRepaintBoundary field isfalse, theneedsPaint gets set totrue and goes on to ask the same forits parent.
  3. TheisRepaintBoundary value ofRenderObject #5 istrue, soneedsPaint staysfalse and the parent marking is stoppedright there.
  4. Then the actual painting phase is started from the top widget marked asneedsPaint. It traverses its children. SinceisRepaintBoundary ofRenderObject #11 isfalse, ittraverses further.
  5. ButisRepaintBoundary ofRenderObject #15 istrue, so the process is stoppedright there.

So we end up repainting render objects #10, #11,and #12.

Let’s take a look at an example where aRepaintBoundary widget can be useful – in aListView. This is the simplified version of theListViewsource code:

class ListView {  ListView({      super.key,      bool addRepaintBoundaries = true,      ... // many more params  });  @override  Widget? build(BuildContext context, int index) {     if (addRepaintBoundaries) {       child = RepaintBoundary(child: child);     }     return child;  }}

TheListView constructor accepts anaddRepaintBoundaries parameter in its constructor, which by default istrue. Later, when building its children, theListView checks this flag, and if it’strue, the child widget is wrapped in aRepaintBoundary widget. This means that during scrolling, the list items don’t get repainted, which makes sense because only their offset changes, not their presentation. TheRepaintBoundary widget can be extremely efficient in cases where you have a heavy yet static widget, orwhen only the location on the screen changes such as during scrolling, transitions, or other animations. However, like many things, it has trade-offs. In order to display the end result on the screen, the widget tree drawing instructions need to be translated into the actual pixel data. Thisprocess is calledrasterization.RepaintBoundary can decide to cache the rasterized pixel values in memory, which is not limitless. Too many of them can ironically lead toperformance issues.

There is also a good way to determine whether theRepaintBoundary is useful in your case. Check thediagnosis field of itsrenderObject via the Flutter inspector tools. If it says something along the lines ofThis is an outstandingly useful repaint boundary, then it’s probably agood idea tokeep it.

Optimizing scroll view performance

There are two important tips for optimizing scrollview performance:

  • First, if you want to build a list of homogeneous items, the most efficient way to do so is by using theListView.builder constructor. The beauty of this approach is that at any given time, by using theitemBuilder callback that you’ve specified, theListView will render only those items that can actually be seen on the screen (and a tiny bit more, as determined by thecacheExtent). This means that if you have 1,000 items in your data list, you don’t need to worry about all 1,000 of them being rendered on the screen at once – unless you have set theshrinkWrap propertytotrue.
  • This leads us to the second tip: theshrinkWrap property (available for various scroll views) forces the scroll view to calculate the layout of all its children, defeating the purpose of lazy loading. It’s often used as a quick fix for overflow errors, but there are usually better ways to address those errors without compromising performance. We’ll cover how to avoid overflow errors while maintaining performance in thenext chapter.

Summary

In this chapter, we explored the relationships between theWidget,Element, andRenderObject trees. We learned how to avoid rebuilds of theWidget andElement trees by scoping theStatefulWidgets and subscriptions to inherited widgets, as well as by caching the widgets viaconst constructors andfinal initializations. We also learned how to limit repaints of the render object subtrees, as well as how to effectively work withscroll views.

InChapter 2, we will explore how to make our already performant interfaces responsive on the ever-growing set of devices. We will cover how sizing and layout work in Flutter, how to fix overflow errors, and how to ensure that your application is usable forall users.

Left arrow icon

Page1 of 5

Right arrow icon
Download code iconDownload Code

Key benefits

  • Cover code structure, app architecture, testing, performance optimization, error handling, and more
  • Get hands on with code examples and exercises to apply design patterns and best practices in real-world scenarios
  • Leverage the intricacies of the Flutter framework to build better apps for your clients
  • Purchase of the print or Kindle book includes a free PDF eBook

Description

Flutter’s rapid adoption by developers and businesses alike has led to an increased demand for skilled developers who can deliver high-quality Flutter apps. Flutter can be a great tool to develop apps for yourself, but you don’t always need to consider things like scalability or business goals as a hobbyist. When you build apps for a business, however, you must use specific tools to help the business achieve its goals. This requires solutions to be fast, reliable, and delivered on time. This book will help you meet these business requirements.You'll begin by learning how to build responsive UIs that work on all devices. Next, you'll delve into state management, understanding how to select the appropriate solution and gaining hands-on experience with InheritedWidget, ChangeNotifier, and BLoC. Later, you'll move on to high-level practices such as layered architecture, dependency injection, and repository patterns. The book will also show you how to develop features of any complexity, such as multithreading and native layer implementations. You'll also learn how to ensure the quality of your solutions by prioritizing testing.By the end of this book, you'll be able to deliver well-architected Flutter projects that are stable, scalable, and maintainable.

Who is this book for?

If you’re a mobile developer who has already realized the potential of Flutter as a tool to solve business problems and are looking forward to leveling up your app-building skills, then this book is for you. Freelancers, agencies, or individuals who have already developed basic applications with Flutter and want to level up their skills to build production-ready apps at scale will find this book helpful. Basic Flutter knowledge and experience in building apps will be beneficial. Moreover, if you’re transitioning from another mobile framework, this book will make the transition easier.

What you will learn

  • Translate business requirements into technical solutions
  • Understand when and where specific design patterns would be applicable
  • Separate concerns into multiple layers with scoped responsibility
  • Build reusable architecture that can be applied to any type of app and supported by various teams
  • Debug and solve problems before they reach the user
  • Prevent production bugs by prioritizing testing during the development cycle
  • Design encapsulated features that are resilient to business requirement changes and software updates

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date :Sep 27, 2024
Length:362 pages
Edition :1st
Language :English
ISBN-13 :9781801074551
Vendor :
Google
Category :
Languages :
Tools :

What do you get with eBook?

Product feature iconInstant access to your Digital eBook purchase
Product feature icon Download this book inEPUB andPDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature iconDRM FREE - Read whenever, wherever and however you want
Product feature iconAI Assistant (beta) to help accelerate your learning

Contact Details

Modal Close icon
Payment Processing...
tickCompleted

Billing Address

Product Details

Publication date :Sep 27, 2024
Length:362 pages
Edition :1st
Language :English
ISBN-13 :9781801074551
Vendor :
Google
Category :
Languages :
Concepts :
Tools :

Packt Subscriptions

See our plans and pricing
Modal Close icon
$19.99billed monthly
Feature tick iconUnlimited access to Packt's library of 7,000+ practical books and videos
Feature tick iconConstantly refreshed with 50+ new titles a month
Feature tick iconExclusive Early access to books as they're written
Feature tick iconSolve problems while you work with advanced search and reference features
Feature tick iconOffline reading on the mobile app
Feature tick iconSimple pricing, no contract
$199.99billed annually
Feature tick iconUnlimited access to Packt's library of 7,000+ practical books and videos
Feature tick iconConstantly refreshed with 50+ new titles a month
Feature tick iconExclusive Early access to books as they're written
Feature tick iconSolve problems while you work with advanced search and reference features
Feature tick iconOffline reading on the mobile app
Feature tick iconChoose a DRM-free eBook or Video every month to keep
Feature tick iconPLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick iconExclusive print discounts
$279.99billed in 18 months
Feature tick iconUnlimited access to Packt's library of 7,000+ practical books and videos
Feature tick iconConstantly refreshed with 50+ new titles a month
Feature tick iconExclusive Early access to books as they're written
Feature tick iconSolve problems while you work with advanced search and reference features
Feature tick iconOffline reading on the mobile app
Feature tick iconChoose a DRM-free eBook or Video every month to keep
Feature tick iconPLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick iconExclusive print discounts

Frequently bought together


Flutter Design Patterns and Best Practices
Flutter Design Patterns and Best Practices
Read more
Sep 2024362 pages
Full star icon5 (4)
eBook
eBook
$9.99$35.99
$44.99
Full-Stack Web Development with TypeScript 5
Full-Stack Web Development with TypeScript 5
Read more
Aug 2024272 pages
Full star icon4.3 (3)
eBook
eBook
$9.99$29.99
$36.99
Java Coding Problems
Java Coding Problems
Read more
Mar 2024798 pages
Full star icon4.5 (13)
eBook
eBook
$9.99$43.99
$54.99
Stars icon
Total$136.97
Flutter Design Patterns and Best Practices
$44.99
Full-Stack Web Development with TypeScript 5
$36.99
Java Coding Problems
$54.99
Total$136.97Stars icon
Buy 2+ to unlock$7.99 prices - master what's next.
SHOP NOW

Table of Contents

18 Chapters
Part 1: Building Delightful User InterfacesChevron down iconChevron up icon
Part 1: Building Delightful User Interfaces
Chapter 1: Best Practices for Building UIs with FlutterChevron down iconChevron up icon
Chapter 1: Best Practices for Building UIs with Flutter
Understanding the difference between declarative and imperative UI design
Everything is a widget! Or is it?
Reduce, reuse, recycle!
Summary
Chapter 2: Responsive UIs for All DevicesChevron down iconChevron up icon
Chapter 2: Responsive UIs for All Devices
Technical requirements
Understanding the Flutter layout algorithm
Designing responsive apps with Flutter
Ensuring accessibility in Flutter apps
Summary
Part 2: Connecting UI with Business LogicChevron down iconChevron up icon
Part 2: Connecting UI with Business Logic
Chapter 3: Vanilla State ManagementChevron down iconChevron up icon
Chapter 3: Vanilla State Management
Technical requirements
Getting to know our Candy Store app
Managing state the vanilla Flutter way
Lifting the state up
Passing around dependencies via InheritedWidget
Interacting with BuildContext in the right place, at the right time
Summary
Chapter 4: State Management Patterns and Their ImplementationsChevron down iconChevron up icon
Chapter 4: State Management Patterns and Their Implementations
Technical requirements
Diving into the world of MVX patterns
Defining criteria for the state management pattern
Embracing data binding with MVVM in Flutter
Implementing the MVVM and MVI patterns with flutter_bloc
Implementing the Segmented State Pattern
Avoiding bugs related to state equality and mutability
Summary
Chapter 5: Creating Consistent NavigationChevron down iconChevron up icon
Chapter 5: Creating Consistent Navigation
Technical requirements
Getting started with navigation basics in Flutter
Leveling up with advanced navigation techniques
Comparing Navigation 1.0 and 2.0
Summary
Part 3: Exploring Practical Design Patterns and Architecture Best PracticesChevron down iconChevron up icon
Part 3: Exploring Practical Design Patterns and Architecture Best Practices
Chapter 6: The Responsible Repository PatternChevron down iconChevron up icon
Chapter 6: The Responsible Repository Pattern
Technical requirements
Introducing the repository pattern
Setting up our first repository
Defining repository interfaces
Building a data source
Creating a local database using Hive
Implementing repository data retrieval
Integrating the repository with our business logic
Caching strategies
Implementing offline mode
Understanding data synchronization
Summary
Chapter 7: Implementing the Inversion of Control PrincipleChevron down iconChevron up icon
Chapter 7: Implementing the Inversion of Control Principle
Technical requirements
Decoupling dependency creation from usage
Implementing the DI pattern via RepositoryProvider
Implementing SL pattern via get_it
Summary
Chapter 8: Ensuring Scalability and Maintainability with Layered ArchitectureChevron down iconChevron up icon
Chapter 8: Ensuring Scalability and Maintainability with Layered Architecture
Technical requirements
Exploring layered architecture
Defining layers and features
Following software design principles
Summary
Chapter 9: Mastering Concurrent Programming in DartChevron down iconChevron up icon
Chapter 9: Mastering Concurrent Programming in Dart
Technical requirements
Dart is single-threaded. Or is it?
Working with Future APIs in Dart
Embracing parallelism with isolates
Summary
Chapter 10: A Bridge to the Native Side of DevelopmentChevron down iconChevron up icon
Chapter 10: A Bridge to the Native Side of Development
Technical requirements
Understanding Flutter app architecture
Working with platform channels
Ensuring type-safe communication via pigeon code generation
Summary
Part 4: Ensuring App Quality and StabilityChevron down iconChevron up icon
Part 4: Ensuring App Quality and Stability
Chapter 11: Unit Tests, Widget Tests, and Mocking DependenciesChevron down iconChevron up icon
Chapter 11: Unit Tests, Widget Tests, and Mocking Dependencies
Technical requirements
Getting started with unit testing
Widget testing fundamentals
Integration testing fundamentals
Mocking dependencies for effective testing
Summary
Chapter 12: Static Code Analysis and Debugging ToolsChevron down iconChevron up icon
Chapter 12: Static Code Analysis and Debugging Tools
Technical requirements
Following coding conventions
Ensuring consistency and preventing bugs with static code analysis
Exploring debugging practices and tools
Summary
IndexChevron down iconChevron up icon
Index
Why subscribe?
Other Books You May EnjoyChevron down iconChevron up icon
Other Books You May Enjoy
Packt is searching for authors like you
Share Your Thoughts
Download a free PDF copy of this book

Recommendations for you

Left arrow icon
.NET MAUI Cookbook
.NET MAUI Cookbook
Read more
Dec 2024384 pages
Full star icon4.3 (3)
eBook
eBook
$9.99$35.99
$44.99
iOS 18 Programming for Beginners
iOS 18 Programming for Beginners
Read more
Dec 2024584 pages
Full star icon4.4 (7)
eBook
eBook
$9.99$35.99
$44.99
Apps and Services with .NET 8
Apps and Services with .NET 8
Read more
Dec 2023798 pages
Full star icon4.7 (16)
eBook
eBook
$9.99$39.99
$49.99
Flutter Design Patterns and Best Practices
Flutter Design Patterns and Best Practices
Read more
Sep 2024362 pages
Full star icon5 (4)
eBook
eBook
$9.99$35.99
$44.99
Mastering Kotlin for Android 14
Mastering Kotlin for Android 14
Read more
Apr 2024370 pages
Full star icon4.8 (10)
eBook
eBook
$9.99$31.99
$39.99
Thriving in Android Development Using Kotlin
Thriving in Android Development Using Kotlin
Read more
Jul 2024410 pages
Full star icon4.5 (6)
eBook
eBook
$9.99$35.99
$44.99
Swift Cookbook
Swift Cookbook
Read more
Jun 2024422 pages
Full star icon5 (3)
eBook
eBook
$9.99$31.99
$39.99
Android Programming for Beginners
Android Programming for Beginners
Read more
Apr 2021742 pages
Full star icon4.2 (12)
eBook
eBook
$32.39$35.99
$43.99
How to Build Android Apps with Kotlin
How to Build Android Apps with Kotlin
Read more
May 2023704 pages
Full star icon4.4 (14)
eBook
eBook
$9.99$83.99
$103.99
Right arrow icon

Customer reviews

Rating distribution
Full star iconFull star iconFull star iconFull star iconFull star icon5
(4 Ratings)
5 star100%
4 star0%
3 star0%
2 star0%
1 star0%
RobertSep 30, 2024
Full star iconFull star iconFull star iconFull star iconFull star icon5
If you have doubts about how to relate patterns such as mvvm, mvi to Flutter. How exactly does UI creation work with the Flutter framework. The book is for you. There are a lot of useful tips but also a comprehensive look at topics such as state management, BuildContext, etc. I don't know if it will be good for a beginner. Rather for someone who has already written an application and wants to do it better in the future.Worth reading, thank you.
Subscriber reviewPackt
BenOct 08, 2024
Full star iconFull star iconFull star iconFull star iconFull star icon5
The level of comprehensive detail and examples in this book are impressive. For mobile app developers, this is a great way to. dive deeper into Flutter and all of it's techniques. Worth reading and practicing the topics here!
Amazon Verified reviewAmazon
Riaz Akbar BachaniDec 30, 2024
Full star iconFull star iconFull star iconFull star iconFull star icon5
Books are in perfect condition as described however delivery was through courier which made me to pay very higher customs duty. If delivery was sent through regular post, as I have paid, I could have saved $40 like my earlier order..
Feefo Verified reviewFeefo
YenTingApr 18, 2025
Full star iconFull star iconFull star iconFull star iconFull star icon5
This book explains things in great detail and offers excellent insights that really help you understand all the fundamentals on a deep level. You have to get this book! I also hope the author continues to update and release future editions. I'm really glad I bought it!
Subscriber reviewPackt

About the 3 authors

Left arrow icon
Profile icon Daria Orlova
Daria Orlova
LinkedIn iconGithub icon
Daria Orlova is a mobile app specialist, who started with native Android in 2015 and discovered Flutter in 2019. She is the co-founder of BBapps, creating “apps that care for you and the planet.” Previously, at Chili Labs, the top mobile agency in the Baltics, together with the team, she delivered 50+ projects, winning awards including RedDot and W3. Daria is an active community member – a Google Developer Expert (GDE) in Flutter and Dart, Flutterista, and WTM ambassador, mentor, technical writer, and public speaker who is passionate about quality tech education. She holds a B.Sc. in computer science.
Read more
See other products by Daria Orlova
Profile icon Esra Kadah
Esra Kadah
LinkedIn iconGithub icon
Esra Kadah is a senior app developer specializing in Flutter, with a passion for integrating programming, UI/UX design, and psychology. She enjoys building and contributing to thriving communities. She is a co-organizer of Flutteristas, Flutter Berlin, and CMX Istanbul, and serves as a Women Techmakers Ambassador. She has delivered over 60 talks, organized more than 150 events, and hosted 40+ streams, collaborating with Google Developer Groups, Google Developer Student Clubs, the Flutter community, Flutteristas, and Women Techmakers.
Read more
See other products by Esra Kadah
Profile icon Jaime Blasco
Jaime Blasco
LinkedIn icon
Jaime Blasco is a seasoned Flutter developer with a deep-rooted passion for crafting exceptional mobile experiences. As a Google Developer Expert (GDE) in Flutter, he possesses an in-depth understanding of the framework and its capabilities. His active involvement in the Flutter community, notably as a co-leader of the Spanish Flutter community, underscores his commitment to knowledge sharing and collaboration. He currently develops the Hypervolt app, a Flutter-based solution that seamlessly connects users to EV chargers. Jaime ensures smooth user interactions and efficient hardware communication, delivering a reliable charging experience.
Read more
See other products by Jaime Blasco
Right arrow icon
Getfree access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

How do I buy and download an eBook?Chevron down iconChevron up icon

Where there is an eBook version of a title available, you can buy it from the book details for that title. Add either the standalone eBook or the eBook and print book bundle to your shopping cart. Your eBook will show in your cart as a product on its own. After completing checkout and payment in the normal way, you will receive your receipt on the screen containing a link to a personalised PDF download file. This link will remain active for 30 days. You can download backup copies of the file by logging in to your account at any time.

If you already have Adobe reader installed, then clicking on the link will download and open the PDF file directly. If you don't, then save the PDF file on your machine and download the Reader to view it.

Please Note: Packt eBooks are non-returnable and non-refundable.

Packt eBook and Licensing When you buy an eBook from Packt Publishing, completing your purchase means you accept the terms of our licence agreement. Please read the full text of the agreement. In it we have tried to balance the need for the ebook to be usable for you the reader with our needs to protect the rights of us as Publishers and of our authors. In summary, the agreement says:

  • You may make copies of your eBook for your own use onto any machine
  • You may not pass copies of the eBook on to anyone else
How can I make a purchase on your website?Chevron down iconChevron up icon

If you want to purchase a video course, eBook or Bundle (Print+eBook) please follow below steps:

  1. Register on our website using your email address and the password.
  2. Search for the title by name or ISBN using the search option.
  3. Select the title you want to purchase.
  4. Choose the format you wish to purchase the title in; if you order the Print Book, you get a free eBook copy of the same title. 
  5. Proceed with the checkout process (payment to be made using Credit Card, Debit Cart, or PayPal)
Where can I access support around an eBook?Chevron down iconChevron up icon
  • If you experience a problem with using or installing Adobe Reader, the contact Adobe directly.
  • To view the errata for the book, see www.packtpub.com/support and view the pages for the title you have.
  • To view your account details or to download a new copy of the book go to www.packtpub.com/account
  • To contact us directly if a problem is not resolved, use www.packtpub.com/contact-us
What eBook formats do Packt support?Chevron down iconChevron up icon

Our eBooks are currently available in a variety of formats such as PDF and ePubs. In the future, this may well change with trends and development in technology, but please note that our PDFs are not Adobe eBook Reader format, which has greater restrictions on security.

You will need to use Adobe Reader v9 or later in order to read Packt's PDF eBooks.

What are the benefits of eBooks?Chevron down iconChevron up icon
  • You can get the information you need immediately
  • You can easily take them with you on a laptop
  • You can download them an unlimited number of times
  • You can print them out
  • They are copy-paste enabled
  • They are searchable
  • There is no password protection
  • They are lower price than print
  • They save resources and space
What is an eBook?Chevron down iconChevron up icon

Packt eBooks are a complete electronic version of the print edition, available in PDF and ePub formats. Every piece of content down to the page numbering is the same. Because we save the costs of printing and shipping the book to you, we are able to offer eBooks at a lower cost than print editions.

When you have purchased an eBook, simply login to your account and click on the link in Your Download Area. We recommend you saving the file to your hard drive before opening it.

For optimal viewing of our eBooks, we recommend you download and install the free Adobe Reader version 9.

Create a Free Account To Continue Reading

Modal Close icon
OR
    First name is required.
    Last name is required.

The Password should contain at least :

  • 8 characters
  • 1 uppercase
  • 1 number
Notify me about special offers, personalized product recommendations, and learning tips By signing up for the free trial you will receive emails related to this service, you can unsubscribe at any time
By clicking ‘Create Account’, you are agreeing to ourPrivacy Policy andTerms & Conditions
Already have an account? SIGN IN

Sign in to activate your 7-day free access

Modal Close icon
OR
By redeeming the free trial you will receive emails related to this service, you can unsubscribe at any time.

[8]ページ先頭

©2009-2025 Movatter.jp