Flutter组件的所有细节All details about Flutter Widgets

2023-05-16

2.2 Introduction to Widgets

2.2.1 Concept of Widget

In the previous introduction, we know that almost all objects in Flutter are widgets. Unlike Views or ViewControllers in native development, widgets in Flutter have a broader concept and can represent not only UI elements but also functional components such as: GestureDetector for gesture detection, Theme for APP theme data passing, and so on, while Views or ViewControllers in native development usually just refer to UI elements. Later, we may use the terms “views” and “components” when describing UI elements, but the reader needs to know that they are widgets, just different expressions in different scenarios. Since Flutter is primarily about building a user interface, for the most part, the reader can think of a widget as a view component rather than making unnecessary distinctions in their concepts.

The UI is built and practiced in the way widgets nested within widgets in Flutter, so remember that everything in Flutter is a Widget.

2.2.2 Widget Interface

In Flutter, the function of a widget is to “describe the configuration of a UI element.” That is, the Widget does not represent the display element that will eventually be drawn on the device screen. The configuration information is the parameters that the Widget receives. The content of the text, the alignment, and the text style are all its configuration information. Let’s take a look at the Widget class declaration:

 // Immutable
abstract class Widget extends DiagnosticableTree {
  const Widget({ this.key });

  final Key? key;

  
  
  Element createElement(a);
  String toStringShort() {
    final String type = objectRuntimeType(this, 'Widget');
    return key = = null ? type : '$type- $key';
  }

  
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;
  }

  
  
  bool operator = =(Object other) = > super = = other;

  
  
  int get hashCode = > super.hashCode;

  static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType = = newWidget.runtimeType
        && oldWidget.key = =newWidget.key; 
  }
  //... 
}
  • @immutable means that the Widget is immutable, which limits the properties defined in the Widget (i.e. configuration information) to be final. Why not allow the properties defined in the Widget to change? This is because the Widget tree is rebuilt in Flutter if the properties change, that is, a new Widget instance is created to replace the old Widget instance, so allowing Widget properties to change is meaningless because the Widget itself will be replaced once its properties change. This is why the properties defined in the Widget must be final.
  • Widget Class inherited from DiagnosticableTree, DiagnosticableTree is the diagnostic tree providing debugging information.
  • Key: A Key is an identifier for Widgets, Elements and SemanticsNodes. It’s used in the canUpdate() method to decide whether to resue the old element or create a new element in the next build time,.
  • createElement(): As mentioned earlier, “One widget can correspond to many Element”; When the Flutter framework constructs the UI tree, it first calls this method to generate corresponding nodes’ Element objects. This method is invoked implicitly by the Flutter framework and is rarely invoked during our development.
  • debugFillProperties(...) overwrites its parent class to set some features of the diagnostic tree.
  • canUpdate(...) is a static method that is mainly used in reusing old elements when the widget tree rebuilds. As we can see from its source code, as long as new Widget and old Widget have the same runtimeType and key, the new widget will be used to update the configuration of the corresponding Element object, or a new Element object will be created to replace the old one.

The details of Key and widget reuse will be discussed in more detail later in the book. For now, just know that explicitly adding a key to a widget may (but not necessarily) make the UI more efficient when rebuilt. You can ignore this parameter for now, as will be explained later in the book.

In addition, the Widget class itself is an abstract class, the core of which is the definition of the createElement() interface. In the development of Flutter, we do not directly subclass the Widget class to implement a new widget instance. We usually do this indirectly by subclass StatelessWidget or StatefulWidget class. StatelessWidget and StatefulWidget are directly derived from the Widget class, and these are important abstract classes in Flutter that introduce two types of widget models. We will focus on these two classes next.

2.2.3 Four Trees in Flutter

Since widgets only describe the configuration of a UI element, who does the actual layout and drawing? The processes of it is as follows:

  1. An Element tree is generated from the Widget tree, and the nodes in the Element tree inherit from the Element class.
  2. Generate a Render tree from the Element tree, and the nodes in the render tree inherit from the RenderObjectClass.
  3. The Layer tree is generated from the render tree and then displayed on the screen. The nodes in the Layer tree inherit from the Layer Class.

The actual layout and rendering logic is in the Render tree, where the Element is the glue that binds the widgets and RenderObjects together and can be understood as an intermediate proxy. To illustrate, consider the following Widget tree:

Container( // A container widget
  color: Colors.blue, // Sets the container background color
  child: Row( // You can arrange child widgets horizontally
    children: [
      Image.network('https://www.example.com/1.png'), // widget that displays pictures
      const Text('A'),],),);

Note that if the background color is set in the Container, a new ColoredBox is created inside the Container to fill the background with the following logic:

if (color ! = null)
  current = ColoredBox(color: color!, child: current);

Inside Image, images are rendered by RawImage, while inside Text, text is rendered by RichText. Therefore, the final structure of Widget tree, Element tree and render tree is shown in Figure 2-2:

在这里插入图片描述

Here’s something to note:

  1. There is a one-to-one mapping between Widget and Element, but not between RenderObject and RenderObject. For example StatelessWidget and StatefulWidget has no corresponding RenderObject.
  2. The render tree generates a Layer tree before goes up to the screen, which we’ll cover later in the principle section. The reader only needs to remember these three trees for the previous section.

2.2.4 StatelessWidget

1. Introduction

In the previous section, we introduced StatelessWidget briefly. StatelessWidget is a relatively simple widget that inherits from the Widget class and overrides the createElement() method:


StatelessElement createElement(a)= > StatelessElement(this);

StatelessElement inherits indirectly from the Element class and corresponds to the StatelessWidget (as its configuration data).

The StatelessWidget is used in scenarios where there is no need to maintain state, and it typically builds the UI by nesting other widgets in the build method, recursively building its nested widgets during the build process. Let’s look at a simple example:

class Echo extends StatelessWidget  {
  const Echo({
    Key? key,  
    required this.text,
    this.backgroundColor = Colors.grey, // The default value is grey
  }):super(key:key);
    
  final String text;
  final Color backgroundColor;

  
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        color: backgroundColor,
        child: Text(text), ), ); }}

The above code implements an Echo widget showing inputted text with specified background color. echo is a built-in Linux command that is used to display the text passed in as an argument.

By convention, widget constructor arguments should use named parameters, and the required parameters in the named parameters should be passed with the required keyword, so that static code analyzers can check them. When inheriting a widget, the first parameter should usually be Key. In addition, if a widget needs to receive child widget(s), then the child or children parameter should usually be placed last in the parameter list. Also by convention, widget properties should be declared final as much as possible to prevent accidental changes.

We can then use it in the following ways:

 Widget build(BuildContext context) {
  return Echo(text: "hello world");
}

Figure 2-3 shows the effect after operation:

2-3

2. Context

The build method takes a context argument, which is an instance of the BuildContext class that represents the context of the current widget in the widget tree, Each widget corresponds to a context object (because each widget is a node in the widget tree). In fact, the context is a handle for the current widget to perform “related operations” at its location in the widget tree, such as providing a way to traverse up the widget tree from the current widget and to find the parent widget by widget type. Here is an example of getting a parent widget in a subtree:

class ContextRoute extends StatelessWidget  {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Context test"),
      ),
      body: Container(
        child: Builder(builder: (context) {
          // Traverse up the widget tree to find the nearest parent widget at the type of Scaffold
          Scaffold scaffold = context.findAncestorWidgetOfExactType<Scaffold> ();// Return AppBar's title, which is actually Text("Context test ").
          return (scaffold.appBar as AppBar).title; }),),); }}

Figure 2-4 shows the effect after operation:

在这里插入图片描述

Note: We will use some of the Context methods as the book goes on, so that the reader can get a sense of it in a specific scenario. We’ll cover more about BuildContext later in the advanced section as well.

2.2.5 StatefulWidget

Like StatelessWidget, StatefulWidget inherits from the Widget class and overwrites the createElement() method, except that it returns a different Element object. In addition, a new interface has been added to the StatefulWidget class, createState().

Let’s look at the class definition for StatefulWidget:

abstract class StatefulWidget extends Widget {
  const StatefulWidget({ Key key }) : super(key: key);
    
  
  StatefulElement createElement(a)= > StatefulElement(this);
    
  
  State createState(a); 
}
  • StatefulElement inherits indirectly from the Element class and corresponds to StatefulWidget (as its configuration data). StatefulElement may call createState() several times to create a State object.

  • createState() is used to create state associated with the StatefulWidget, which may be called several times during the StatefulWidget’s life cycle. For example, when a StatefulWidget is inserted into multiple locations in the widget tree at the same time, the Flutter framework calls this method to generate a separate State instance for each location, essentially one StatefulElement corresponds to one State instance.

    In the StatefulWidget, there is a one-to-one relationship between the State object and the StatefulElement, so in the Flutter SDK documentation you often see statements like “remove the State object from the tree” or “insert the State object into the tree”. The tree in this case refers to the Element tree generated from the widget tree. The Flutter SDK documents often refer to the word “tree” and we can tell which tree is being referred to depending on the context. In fact, the ultimate goal of any tree is to describe the structure of the UI and to draw information, so when the idea of a tree is encountered in Flutter, unless otherwise stated, it can be understood as “a tree of nodes that constitutes a user interface”, and the reader does not have to be torn between these concepts.

2.2.6 State

1. Introduction

A StatefulWidget class corresponds to a State class. State represents the state maintained by the StatefulWidget. The state information stored in State can be:

  1. It can be read synchronously when the widget is built.
  2. It can be changed during the widget lifecycle. And we can call setState() method manually when the State is changed to notifies the Flutter framework of the change, then the Flutter framework will call the build method again after receiving the message to rebuilds the widget tree to update the UI.

There are two common attributes in State:

  1. widget, which represents the widget instance associated with the State instance, set dynamically by the Flutter framework. Note that this association is not permanent, because during the application lifecycle, the widget instance at a node in the UI tree may change when rebuilt, but the State instance is only created when first inserted into the tree. When rebuilt, if the widget has been modified, The Flutter framework dynamically sets State.widget as a new widget instance.
  2. context, which is the BuildContext corresponding to StatefulWidget and is equivalent to the BuildContext of StatelessWidget.

2. State life cycle

Understanding the life cycle of State is important to the development of flutter. In order to impress the reader, we demonstrate the life cycle of State through an example in this section. In the following example, again using the counter function as an example, we implement a CounterWidget component that we click to increment the counter by 1. Since we want to save the counter’s numerical state, we should inherit the StatefulWidget as follows:

class CounterWidget extends StatefulWidget {
  const CounterWidget({Key? key, this.initValue = 0});

  final int initValue;

  
  _CounterWidgetState createState(a)= > _CounterWidgetState(a); 
}

CounterWidget receives an initValue integer argument, which represents the initial value of the counter. Let’s look at the code for State:

class _CounterWidgetState extends State<CounterWidget> {
  int _counter = 0;

  
  void initState() {
    super.initState(a);// Initialize the state
    _counter = widget.initValue;
    print("initState");
  }

  
  Widget build(BuildContext context) {
    print("build");
    return Scaffold(
      body: Center(
        child: TextButton(
          child: Text('$_counter'),
          // The counter increases after clicking
          onPressed:(a)= > setState(()= > ++_counter,
          ),
        ),
      ),
    );
  }

  
  void didUpdateWidget(CounterWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("didUpdateWidget ");
  }

  
  void deactivate() {
    super.deactivate(a);print("deactivate");
  }

  
  void dispose() {
    super.dispose(a);print("dispose");
  }

  
  void reassemble() {
    super.reassemble(a);print("reassemble");
  }

  
  void didChangeDependencies() {
    super.didChangeDependencies(a);print("didChangeDependencies"); }}

Next, we create a new route in which we display only one CounterWidget:

class StateLifecycleTest extends StatelessWidget {
  const StateLifecycleTest({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return CounterWidget();
  }
}

We run the application and open the routing page. When the new routing page opens, a number 0 appears in the center of the screen, and the console log outputs:

I/flutter ( 5436): initState
I/flutter ( 5436): didChangeDependencies
I/flutter ( 5436): build

As you can see, the initState method is first called when the StatefulWidget is inserted into the widget tree.

Then we click ⚡️ button to hot reload, the console output log is as follows:

I/flutter ( 5436): reassemble
I/flutter ( 5436): didUpdateWidget 
I/flutter ( 5436): build

You can see that neither initState nor didChangeDependencies is called, while didUpdateWidget is called.

Next, we remove CounterWidget from the widget tree and change the StateLifecycleTest build method to:

 Widget build(BuildContext context) {
  // Remove the counter
  //return CounterWidget ();
  // Return any Text()
  return Text("xxx");
}

Then hot reload, log as follows:

I/flutter ( 5436): reassemble
I/flutter ( 5436): deactive
I/flutter ( 5436): dispose

As you can see, when CounterWidget is removed from the widget tree, deactive and dispose are called in turn.

Here’s a look at the individual callback functions:

  • initState: The callback is called when the widget is first inserted into the widget tree. The Flutter framework calls this callback only once for each State object, so it usually does some one-time operations within the callback, such as state initialization and subscribing to event notifications from the subtree. Cannot call BuildContext.DependOnInheritedWidgetOfExactType in this callback (the method is used to get an above InheritedWidget closest to the current widget when traversing up the widget tree, we will describe the InheritedWidgets in a later section). The reason is that the InheritFromWidgets in the widget tree may also change after initialization. So the correct way is to call it in the build() method or didChangeDependencies().
  • didChangeDependencies() : called when the dependencies of the State object change; For example: InheritedWidget was included in build() method (Chapter 7 dives into InheritedWidget), and it changed in subsequent build() callbacks, then all children of the InheritedWidget 's didChangeDependencies() methods are called. A typical scenario is that the Flutter framework notifies widgets to call this callback when the system language Locale or application theme changes. Note that the corresponding didChangeDependencies method is also called when the widget is created and mounted at the first time (including recreations).
  • build() : This callback, which readers should be familiar with by now, is primarily used to build widget subtrees and is called in the following scenarios:
    1. After the initState()call.
    2. After the `didUpdateWidget()call.
    3. After the `setState()call.
    4. After the didChangeDependencies()call.
    5. The State object is removed from one position in the tree (deactivate is called), then it is reinserted to another position in the tree, then the build() is called.
  • reassemble() : This callback is provided specifically for development debugging and is called on hot reload, which is never called in Release mode.
  • didUpdateWidget() : When the widget is rebuilt, the Flutter framework calls widget.canupdate() to detect the old and new nodes at the same location in the widget tree and then determine if an update is needed, this callback is called if widget.canupdate() returns true. As mentioned earlier, widget.canUpdate() returns true if the key and runtimeType of the new and old widgets are equal seperately, That is, didUpdateWidget() is called when the key and runtimeType of the new and old widgets are equal seperately.
  • deactivate() : This callback is called when the State object is removed from the tree. In some scenarios, the Flutter framework inserts the State object back into the tree, such as when the subtree containing the State object is moved from one location in the tree to another location (this can be done through GlobalKey). The dispose() method is then called if it is not reinserted into the tree.
  • dispose() : called when a State object is permanently removed from the tree; Resources are typically released in this callback.

Figure 2-5 shows the life cycle of StatefulWidget.

在这里插入图片描述

Note: When you overwrite methods from StatefulWidget, you call the parent method in the subclass method for any parent method that contains the @mustCallSuper annotation.

2.2.7 Obtaining the State object from the widget tree

Because the logic of the StatefulWidget is in its State, many times we need to get the State object of the StatefulWidget to call methods. For example, the ScaffoldState class corresponding to the Scaffold component defines a method for opening SnackBar (the hint bar at the bottom of the routing page). There are two ways to get the State object of the parent StatefulWidget in the tree of child widgets.

1. Obtain the value from Context

The context object with a findAncestorStateOfType () method, this method can upward from the current node along the widget tree search specified type StatefulWidget corresponding State object. Here is an example of how to open SnackBar:

class GetStateObjectRoute extends StatefulWidget {
  const GetStateObjectRoute({Key? key}) : super(key: key);

  
  State<GetStateObjectRoute> createState(a)= > _GetStateObjectRouteState(a); }class _GetStateObjectRouteState extends State<GetStateObjectRoute> {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Get State object in subtree"),
      ),
      body: Center(
        child: Column(
          children: [
            Builder(builder: (context) {
              return ElevatedButton(
                onPressed: () {
                  // Finds the ScaffoldState object corresponding to the most recent Scaffold of the parent
                  ScaffoldState _state = context.findAncestorStateOfType<ScaffoldState> ()!;
                  // Open the drawer menu
                  _state.openDrawer(a); }, child: Text('Open Drawer Menu 1'),); }), ], ), ), drawer: Drawer(),); }}

In general, if the State of a StatefulWidget is private (and should not be exposed to the outside world), then our code should not get its state object directly. If the State of the StatefulWidget is what you want to expose (and usually some component action), you can get its state object directly. But by the context. FindAncestorStateOfType StatefulWidget state method is universal, we can’t state whether private StatefulWidget specified in the grammatical level, Therefore, there is a default convention in the development of Flutter: if the State of the StatefulWidget is intended to be exposed, a static method of StatefulWidget should be provided to obtain its state object, which can be obtained directly by the developer. If the State does not want to be exposed, the of method is not provided. This convention is everywhere in the Flutter SDK. So, the Scaffold in the example above also provides an of method that we can actually call directly:

Builder(builder: (context) {
  return ElevatedButton(
    onPressed: () {
      // ScaffoldState is obtained directly through the of static method
      ScaffoldState _state=Scaffold.of(context);
      // Open the drawer menu
      _state.openDrawer(a); }, child: Text('Open Drawer Menu 2'),); }),

For example, if we want to display the snack bar, we can do the following:

Builder(builder: (context) {
  return ElevatedButton(
    onPressed: () {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text("I'm SnackBar.")),); }, child: Text('Show SnackBar'),); }),

After running the above example, click “Show SnackBar”, and the effect is as shown in Figure 2-6:

在这里插入图片描述

2. Via GlobalKey

There is also a universal method of obtaining State objects for Flutter – via GlobalKey! There are two steps:

  1. Add GlobalKey to the target StatefulWidget.

    // Define a GlobalKey. Since GlobalKeys are globally unique, we use static variable storage
    static GlobalKey<ScaffoldState> _globalKey= GlobalKey(a); ...Scaffold(
        key: _globalKey , // Set the key...)
    
  2. Get the State object via GlobalKey

    _globalKey.currentState.openDrawer(a)
    

GlobalKey is a mechanism provided by Flutter to reference elements throughout the App. If a widget is set to GlobalKey, we can get the widget object via globalKey.currentWidget, globalkey. currentElement to get the element object corresponding to the widget, If the current widget is a StatefulWidget, you can use globalKey.currentState to get the state object for the widget.

Note: Using GlobalKey is expensive and should be avoided if there are other alternatives. In addition, the same GlobalKey must be unique in the entire widget tree and cannot be repeated.

2.2.8 Using the RenderObject to customize Widgets

Both StatelessWidget and StatefulWidget are used to compose other components. They do not have a corresponding RenderObject. Many of the basic components in the Flutter library are not implemented through StatelessWidget and StatefulWidget, such as Text, Column, Align, etc., which are like building blocks, StatelessWidget and StatefulWidget can build blocks in different ways, but only if there are blocks, which are implemented using a custom RenderObject. In fact, the original way that Flutter defines components is by defining a RenderObject, with StatelessWidget and StatefulWidget as just two helper classes. Here’s a quick demonstration of how to define a component using a RenderObject:

class CustomWidget extends LeafRenderObjectWidget{
  
  RenderObject createRenderObject(BuildContext context) {
    // Create a RenderObject
    return RenderCustomObject(a); }
  void updateRenderObject(BuildContext context, RenderCustomObject  renderObject) {
    // Update the RenderObject
    super.updateRenderObject(context, renderObject); }}class RenderCustomObject extends RenderBox{

  
  void performLayout() {
    // Implement the layout logic
  }

  
  void paint(PaintingContext context, Offset offset) {
    // Implement drawing}}

If the component does not contain child components, then we can directly inherit from LeafRenderObjectWidget, which is a subclass of RenderObjectWidget, and RenderObjectWidget inherits from widgets. Let’s look at its implementation:

abstract class LeafRenderObjectWidget extends RenderObjectWidget {
  const LeafRenderObjectWidget({ Key? key }) : super(key: key);

  
  LeafRenderObjectElement createElement(a)= > LeafRenderObjectElement(this);
}

The widget implements the createElement method, which creates an Element object of type LeafRenderObjectElement for the component. If the custom widget can contain child components, you can choose according to the number of child components to inherit SingleChildRenderObjectWidget or MultiChildRenderObjectWidget, They also implement the createElement() method, which returns a different type of Element object.

We then override the createRenderObject method, which is defined in RenderObjectWidget and is called by the component’s corresponding Element (when building the render tree) to generate the render object. Our main task is to implement the render object class returned by createRenderObject, in this case RenderCustomObject. The updateRenderObject method is a callback used to update the component render object if the component tree state changes but the RenderObject does not need to be recreated.

RenderCustomObject class inherits from RenderBox, and RenderBox inherits from RenderObject. We need to implement layout, rendering, event response and other logic in RenderCustomObject. The knowledge will be covered throughout the book, but don’t worry, we will introduce it in later chapters.

2.2.9 Introduction to the Flutter SDK built-in component library

Flutter provides a rich and powerful base component library on top of which Flutter provides a Material style (the default visual style for Android) and a Cupertino style (the visual style for iOS) component library. To use the base component library, you need to lead in:

import 'package:flutter/widgets.dart';

Let’s take a look at the common components.

1. Basic components

  • Text: This component lets you create a formatted text.
  • Row, Column: These layout class widgets with elastic Spaces allow you to create flexible layouts in both horizontal (Row) and vertical (Column) directions. The design is based on the Flexbox layout model for Web development.
  • Stack: Instead of linear layout (and Android)FrameLayoutSimilar), Stack allows sub-widgets to Stack, which you can use PositionedTo locate them relative toStackThe position of the top, bottom, left and right sides. Stacks is designed based on the absolute positioning layout model used in Web development.
  • Container: Container lets you create rectangular visual elements. A Container can decorate a BoxDecoration, such as a background, a border, ora shadow. A Container can also have margins, padding, and constraints that apply to its size. In addition, Container can transform it in three dimensions using a matrix.

2. Material component

Flutter offers a rich set of Material components that help us build applications that adhere to the Material Design design specifications. The Material application starts with the MaterialApp component, which creates the necessary components at the root of the application, such as the Theme component, which is used to configure the theme of the application. Whether or not to use the MaterialApp is entirely optional, but it is a good practice to use it. In the previous example, we have used several Material components such as: Scaffold, AppBar, TextButton, and so on. To use the Material component, you need to introduce it:

import 'package:flutter/material.dart';

3. Cupertino components

The Flutter also offers a rich set of Cupertino-style components, although not yet as rich as the Material component, but it is still being refined. It is worth noting that there are some Material components in the Material library that can switch presentation styles based on the actual running platform, such as MaterialPageRoute, which uses Android’s default page switching animation (bottom up) when switching routes. On iOS, it uses iOS’s default page-switching animation (from right to left). Since we didn’t have an example of the Cupertino component in the previous example, let’s implement a simple Cupertino component style page:

Import the cupertino widget library
import 'package:flutter/cupertino.dart';

class CupertinoTestRoute extends StatelessWidget  {
  
  widget build(BuildContext context) {
    return CupertinoPageScaffold(
      navigationBar: CupertinoNavigationBar(
        middle: Text("Cupertino Demo"),
      ),
      child: Center(
        child: CupertinoButton(
            color: CupertinoColors.activeBlue,
            child: Text("Press"),
            onPressed:() {}),); }}

Below (Figure 2-7) is a screenshot of what the page looks like on iPhoneX:

在这里插入图片描述

There are two types of widgets with Flutter: StatefulWidget and StatelessWidget. It is important to understand the difference between them. Widgets will be the cornerstone of building Flutter.

Flutter provides a wealth of components that can be used as needed in actual development without worrying about making your application’s installation package bigger by introducing too many component libraries. This is not web development. dart will only compile the code you use at compile time. Since both Material and Cupertino are on top of the base component library, if we introduce either of those in our application, we don’t need to introduce flutter/ widgets.dart because they’ve already been introduced internally.

As an aside, the examples later in this chapter will use some layout class components, such as Scaffold, Row, Column, and so on, which will be covered in detail in the Layout Class Components chapter.


is is not web development. dart will only compile the code you use at compile time. Since both Material and Cupertino are on top of the base component library, if we introduce either of those in our application, we don’t need to introduce flutter/ widgets.dart because they’ve already been introduced internally.

As an aside, the examples later in this chapter will use some layout class components, such as Scaffold, Row, Column, and so on, which will be covered in detail in the Layout Class Components chapter.


Du Wen works for the original Flutter in Practice 2nd Edition in Chinese Language. You can find the original Github project flutter_in_action_2nd there, and the translated English version in my Github project Flutter_In_Practice_V2_English here. I find this book is really useful and try to translate and tweak the book content to benefit more Flutter developers like me.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Flutter组件的所有细节All details about Flutter Widgets 的相关文章

  • MAC【Android studio】Gradle download:kotlin-compiler-embeddable-1.3.70.jar下载太慢

    采用策略去Maven仓库下载资源 更新到本地进行加载 1 进入仓库网址链接 https mvnrepository com 点击下载如图 2 配置下载jar文件到 gradle文件中 文件路径 Users 34 用户名 34 gradle
  • 线程安全是如何实现的?

    实现线程安全与代码编写有很大的关系 xff0c 但虚拟机提供的同步和锁机制也起到了至关重要的作用 互斥同步 互斥同步 xff08 Mutual Exclusion amp Synchronized xff09 是一种最常见也是最主要的并发正
  • 2022,音乐号自媒体起号涨粉保姆级攻略,这是个收获的季节。

    前言 最近遇到好多朋友说想做短视频自媒体 xff0c 但找了很多资料 xff0c 甚至报了学习班也没什么起色 xff0c 这不前天一个姐姐说他家姑娘想做抖音 xff0c 俩月了没啥粉丝也没啥播放量 xff0c 看到我的音乐号了 xff0c
  • 短视频消重去重九种方法,组合使用原创度更高,各平台轻松过原创

    想做好视频去重 xff0c 首先要了解视频查重的机制 网上流传的检查MD5其实已经过时了 xff0c 现在MD5已经是过去式 查重要对视频的方方面面进行检查 xff0c 包括但不限于 xff1a 视频的标题 xff0c 封面 xff0c 描
  • 不直播不露脸也能做短视频变现,想挣点生活费试试这些玩法

    大家好 xff0c 我是 64 我赢助手短视频运营 xff0c 最近一直有小伙伴问我 xff0c 做了好久的短视频一直不赚钱 xff0c 今天总结了一下 xff0c 做了90天短视频还没赚了试试这5种易变现的玩法 新手不直播不露脸也能做短视
  • 科普一下:拍抖音需要什么设备,可能用到的设备合集

    大家好 xff0c 我是 64 我赢助手短视频运营 xff0c 抖音是现在最火的短视频平台之一 xff0c 几乎每个人都在用它 xff0c 抖音视频的发展离不开手机 但你知道吗 xff1f 手机拍摄有很多方面的限制 xff0c 比如手持稳定
  • 短视频上热门技巧总结,这样做你也可以快速上热门。

    最近开始做短视频 xff0c 找了很多短视频运营创作技巧 xff0c 但能上热门的只有那么几个 xff0c 经过近一周的分析 xff0c 结合了我赢上短视频运营创作技巧 xff0c 得到了以下几个经典技巧合集 xff1a 学会一个就值了 首
  • 不要再被骗了,解密短视频里追剧看电影都能赚钱的坑。

    不要再被骗了 解密短视频里追剧看电影都能赚钱的坑 最近刷短视频刷到了好多抖音追剧看电影每天收几百 xff0c 还有截图 xff0c 还有怎么实操的视频 关键不是刷到一个两个 xff0c 是几十个还有越来越多的趋势 都说新入门实操 xff0c
  • 视频号的视频怎么下载到手机?不录屏也不找手机缓存,一键下载。

    你有没有看到有用的视频 xff0c 只收藏起来 xff0c 最后又找不到的时候 前天给朋友找了一个过年快速整理家具杂物的教程 xff0c 谁知道原作者第二天把视频给删掉了 xff0c 朋友说没有来得及看 昨天发给了朋友一个最新的教程 xff
  • 2023年做好自媒体的五个步骤,一步一个脚印稳妥找到结果。

    2023年做好自媒体的五个步骤 一步一个脚印稳妥找到结果 我不管你现在什么负债 破产 xff0c 还是迷茫焦虑 xff0c 通通别管了 xff0c 听话照做执行 看完我这篇内容 xff0c 2023年你还做不起来 xff0c 你就别打嘴炮
  • 2023视频号视频怎么下载到手机?最新验证过的可用方法汇总

    视频号视频怎么下载到手机 xff1f 今天又想下载一个视频号的视频 xff0c 找到收藏已久的视频号视频下载方法 xff0c 结果发现很多方法已经不能用 xff0c 一些能用的也开始收费了 只好重新寻找适合2023最新的几种方法解决视频号下
  • Android系统签名制作

    系统环境 Ubuntu18 04 首先要有对应系统的源码 源码目录build target product security将platform pk8和platform x509 pem copy出来放到一个目录下生成shared priv
  • 使用Cropper进行图片剪裁上传

    在项目中 xff0c 需要多上传的图片按照比例和尺寸进行裁剪 xff0c 这类场景在一些CMS系统中是比较常见的 xff0c 尤其是大部分的文章现在要适配PC Mobile两种平台 xff0c 文章的封面图等便需要按照尺寸做裁剪 xff0c
  • Element-UI消息提示组件Message在Vuex中的调用实现

    在最近的项目开发中 xff0c 前端部分使用 Vue 开发 xff0c 整个页面基于 Element UI 实现 由于是单页面多组件应用 xff0c 使用了 Vuex 做状态管理 为了页面交互的友好和风格的统一 xff0c 消息提醒使用 E
  • Fork原项目新增分支的同步和推送

    在 Github 或者 Gitlab Fork 项目以后 xff0c 原项目增加了新的分支 xff0c 我们可以通过以下流程将分支同步下来 本项目前提假设我们设置了 remote 的名称为 up 与源项目关联 git remote v or
  • Elasticsearch文档版本冲突原理与解决

    一般我们在更新文档时 xff0c 主要的操作流程时 xff1a 读取文档 gt 修改 gt 提交保存 数据中心等保存的都是最新一次提交的内容 大部分时候 xff0c 这都没有什么问题 但是如果两个或更多的请求同时修改一个文档时 xff0c
  • 随笔

    沟通 跨部门沟通 xff0c 首先应确立沟通的目的是双方的有效配合和问题解决的方向 方法 xff0c 去除程序员们自带的问题责任论 xff0c 不能按照 不是我的问题 xff0c 我这OK xff0c 跟我无关 这样的思路去讨论和解决问题
  • Linux内存消耗

    原文 xff1a https web archive org web 20120520221529 http emilics com blog article mconsumption html 本文主要描述如何通过一个合理的方法来测量li
  • ubuntu22 允许root权限远程登录设置

    在 etc ssh sshd config文件中设置 注释 PermitRootLogin prohibit password 添加PermitRootLogin yes
  • 2021年完美解决Gradle下载慢的问题(Android Studio)

    1 问题背景 安卓开发过程中 xff0c 经常会遇到Gradle Wrapper包下载缓慢导致项目无法编译影响开发效率问题 xff0c 极端情况下下载速度只有10kb xff0c 下载一个特定版本的Gradle需要半个小时以上 xff0c

随机推荐