详细尝鲜flutter

flutter

161·`·由于官方的汉化文档感觉还是有很多没有汉化的地方 ,所以自己打一遍的同时写下了以下笔记

在这里插入图片描述

社区生态

官方文档 所有的控件:Widget 目录 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter

官方论坛的教程 Flutter Widget框架概述 - Flutter中文网 (flutterchina.club)

全球开发者写的flutter插件查找网站 Easy Flutter Pubs - Finding Flutter packages more easier (pubdev.top)

基本上各个开发者开发的查找凭借就可以凭借一款不错的app了

由于flutter是使用dart语言,所以需要熟悉dart (很简单 80%的java 和20%js的感觉)

目录结构:

my_flutter_app/
├── android/
├── build/
├── ios/
├── lib/
│ ├── main.dart
│ ├── pages/
│ │ ├── home_page.dart
│ │ ├── settings_page.dart
│ │ └──…
│ ├── models/
│ │ ├── user.dart
│ │ └──…
│ ├── services/
│ │ ├── api_service.dart
│ │ └──…
│ ├── utils/
│ │ ├── constants.dart
│ │ ├── helpers.dart
│ │ └──…
│ └── widgets/
│ ├── custom_button.dart
│ └──…
├── test/
├──.gitignore
├──.metadata
├── pubspec.yaml
├── README.md
└── analysis_options.yaml

pubspec.yaml :管理第三方依赖

还有一个lock 当运行编译产生文件

目录结构

而 ios 和 android 文件夹中的代码是用于集成 Flutter 应用到 iOS 和 Android 平台的原生部分。
所有的adrt代码写到lib目录 编译运行时候

快速入门

android studo 下载flutter插件后 进入lib目录进行启动

main是入口 app.run 是将widget(控件)渲染到屏幕上 flutter应用中的所有显示效果都是采用控件的形式
比如改成 显示一个文本

  /**
   * widget 组件
    // * runapp 渲染组件到屏幕上
    // * 渲染构造眼熟时候就要求静态化
    // */
  runApp(const Text("你好 我是显示文本的组件",
      textDirection:TextDirection.ltr,
      style: TextStyle(
        shadows:[],
        fontSize: 30,
        color: Colors.pink,
      )
  ));

值得注意的是需要表明 标明控件的方向, 否则会进行报错

布局应用app(脚手架)

void main() {

  /**
   * aterialApp 是 Flutter 框架中的一个顶级组件,用于构建一个基于 Material Design 风格的应用程序。Material Design 是 Google 推出的设计规范,强调简洁、响应式、多平台的一致用户体验,Flutter 提供了 MaterialApp 这个类来帮助开发者快速构建符合 Material Design 的应用。

      MaterialApp 作用
      MaterialApp 是 Flutter 应用的入口,负责管理应用的路由、主题、导航、以及一些全局的设置。它类似于 Android 中的 Application 类或是 iOS 中的 AppDelegate。
      为app 开发提供了模板 在其中写的组件 除开防线组件不需要写模板
   */

//1. 定义 TextTheme
  TextTheme textTheme = const TextTheme(
    bodyLarge:  TextStyle(fontSize: 18, color: Colors.red),
    // 可以定义更多的文本样式...
  );

    runApp(MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.blue,
//      2.  全局使用
        textTheme:textTheme),

      //为 Material Design 微件创建可视化基架。
      home: Scaffold(
        appBar: AppBar(
          //左边的图标
          leading: const Icon(Icons.access_alarms),
          // 中间区域 title
          title: const Center(child:   Text("测试app主页",
                style: TextStyle(
                  shadows:[],
                  color: Colors.cyan,
                )),
          ),
//          右边区域
          actions:[
            //3.  全局使用
            Text("详细查看", style: textTheme.bodyLarge, ),
            const Icon(Icons.ac_unit_sharp),
          ],
        ),

        // 内容部分   垂直布局  采用容器列进行布局

          body: Column(
            children: [
              Expanded(
                flex: 40, // 表示占据的比例
                child: Container(
                  color: Colors.red,
                  child: Center(
                    child: Column(  // 使用 Column 允许在同一区域放置多个控件
                      mainAxisAlignment: MainAxisAlignment.center,  // 垂直居中对齐
                      children: [
                        Container(
                          width: 100,
                          height: 100,
                          color: Colors.amberAccent,
                          child: TextButton(
                            onPressed: () => print("用户点击了按钮"),
                            onLongPress: () => print("长按触发事件"),
                            child: const Text("点击即可"),
                          ),
                        ),
                        const SizedBox(height: 20),  // 增加一个空隙


                        Container(
                          width: 100,
                          height: 100,
                          color: Colors.lightBlue,
                          child: TextButton(
                            onPressed: () => print("用户点击了另一个按钮"),
                            child: const Text("另一个按钮"),
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
              ),
//              占位分割符
             const Spacer(),

              Expanded(
                flex: 20,
                //直接监听区域事件
                child: GestureDetector(
                  // 单击
                  onTap: () {
                    print("监听点击了按钮");
                  },

                  child: Container(
                    color: Colors.amber,
                    child: Container(
                      color: Colors.blue,
                      child:const Center(child: Text("蓝色容器")),
                    ),
                  ),
                ),
              ),
             const Spacer(),
              Expanded(
                flex: 20,
                child: Container(
                  color: Colors.green,
                  child: Center(child: Text("绿色容器")),
                ),
              ),
            ],
          ),





          //浮动按钮 可以点击 点击时触发事件 容器预一营好的布局 改按钮是在容器中 右下角
        floatingActionButton: FloatingActionButton(

          child:  Icon(Icons.account_balance_wallet_sharp),
        onPressed: () {
          print("点击了按钮");
        },
        tooltip: "长按触发事件",
        ),

        //底部的导航栏 有items 属性 当前位置 索引 点击时时间等
        bottomNavigationBar: buildBtoomnNavigationBar()
      ),
    ));
}

Widget buildBtoomnNavigationBar() {
  return BottomNavigationBar(
    items: const <BottomNavigationBarItem>[
      BottomNavigationBarItem(
        icon: Icon(Icons.home),
        label: '首页',
      ),
      BottomNavigationBarItem(
        icon: Icon(Icons.search),
        label: '搜索',
      ),
      BottomNavigationBarItem(
        icon: Icon(Icons.person),
        label: '个人中心',
      ),
    ],
    // 初始索引位置
    currentIndex: 0, // 选中的索引
    onTap: (index) {
      // 处理点击事件
      // 例如,切换页面

      print('点击了第$index 个按钮');
    },
  );
}

MaterialApp 就是一个快入搭建material风格的app脚手架 其中可以有各种空间
Scaffold 是 Flutter 中的一个基本结构组件,提供了一个应用程序的框架。它包含一个 AppBar(应用栏)、一个主内容区域(body)和一个底部导航栏(bottomNavigationBar)。
它让你更方便地构建具有 Material Design 风格的界面
Scaffold 的主要组成部分
Scaffold 提供了以下几个主要部分:
appBar:位于屏幕顶部的应用栏(AppBar)。
body:屏幕的主要内容区域。
bottomNavigationBar:位于屏幕底部的导航栏(BottomNavigationBar)。
floatingActionButton:浮动操作按钮(FloatingActionButton)。
drawer:侧边抽屉(Drawer)。
endDrawer:右侧边抽屉(EndDrawer)。
backgroundColor:背景颜色。
resizeToAvoidBottomInset:是否调整大小以避免底部内边距。
resizeToAvoidBottomPadding:是否调整大小以避免底部填充。
primary:是否是主要的屏幕。
extendBody:是否扩展到屏幕边缘。
extendBodyBehindAppBar:是否在 AppBar 后面扩展内容。
persistentFooterButtons:底部固定的按钮。
navigationMode:导航模式。
bottomSheet:底部表单(BottomSheet)。

这样看的话 flutter 就像是把各个ui控件拼接到各个布局组成app

并且跟web前端很相似

由于每一个布局的位置的参数都是控件 可以进行拆分成方法

所以这个给文档主要就是说各个控件以及相关api

布局控件

再任何布局前,官方推荐根布局嵌套一个safearea(安全区),自适应,不会被状态栏给挡住的内容区域

body:SafeArea(
        child: ListView(......)
)

在body部分 ,这部分的控件主要是手机的主要显示区域 ,往往是一个列包裹多个行 (手机是竖屏显示)

contaier

body 部分确实经常使用像 Container 这样的组件来布局页面,但与前端开发中的 div 不完全相同。Flutter 提供了许多灵活的布局控件,开发者在实际开发中会根据需求来组合使用这些控件,而不仅仅依赖 Container

Flutter 和前端布局的对比

  • 前端 div:在前端开发中,div 是一种非常基础的布局标签,几乎什么都可以包裹。开发者通常使用 CSS 来为 div 设置布局属性,比如 flexgridpaddingmargin 等。
  • Flutter ContainerContainer 是 Flutter 中类似 div 的通用容器控件。它可以包裹其他控件,并允许设置 paddingmarginbordercolorwidthheight 等属性。除此之外,Flutter 提供了更多的布局控件,如 RowColumnStackExpanded 等,专门用于实现不同的布局。

实际开发中的 body 布局

在实际开发中,body 部分通常使用多个 Flutter 的布局控件进行嵌套组合,而不仅仅是使用 Container。这使得布局更加灵活和响应式。下面是一些常见的布局控件及其用法:

1. ColumnRow

ColumnRow 是最常见的布局控件,用于垂直或水平排列子组件。它们可以像前端的 flex 布局一样使用(不可以直接设置宽度高度 需要根据子容器来)

body: Column(
  children: [
    Container(
      width: double.infinity,
      height: 200,
      color: Colors.red,
      child: Center(child: Text("顶部区域")),
    ),
    Row(
      children: [
        Expanded(child: Container(color: Colors.blue, height: 100)),
        Expanded(child: Container(color: Colors.green, height: 100)),
      ],
    ),
    Container(
      width: double.infinity,
      height: 200,
      color: Colors.yellow,
      child: Center(child: Text("底部区域")),
    ),
  ],
)

并且row和col 都有俩个相同的api

主轴和副轴的对称方式

  		 mainAxisAlignment: MainAxisAlignment.spaceBetween, // 主轴对齐方式
          crossAxisAlignment: CrossAxisAlignment.center, // 副轴对齐方式

比如当布局时row 时候,主轴就是行,副轴就是列

当然这种包裹的子组件是children 如果类似多个控件数组,有多种方式批量生成

list.generate

   List<String> items = ['Apple', 'Banana', 'Cherry'];
   List<Widget> widgetList = List.generate(items.length, (index) {
     return Text(items[index]);
   });
   

map (类似java stream 流)

   List<String> items = ['Apple', 'Banana', 'Cherry'];
   List<Widget> widgetList = items.map((item) => Text(item)).toList();
   




builder

   ListView.builder(
     itemCount: items.length,
     itemBuilder: (BuildContext context, int index) {
       return Text(items[index]);
     },
   );
   

最简单的for 构建控件数组

   List<Widget> widgetList = [];
   for (var item in items) {
     widgetList.add(Text(item));
   }
   
2. Stack

Stack 类似于 HTML/CSS 中的 position: absolute,允许在同一个布局中叠加多个组件。

body: Stack(
  children: [
    Container(
      width: double.infinity,
      height: 300,
      color: Colors.blue,
    ),
    //搭配这个属性可以实现类似z-index:999的效果
    Positioned(
      top: 100,
      left: 50,
      child: Container(
        width: 100,
        height: 100,
        color: Colors.red,
      ),
    ),
  ],
)
3. ListView

对于需要滚动的内容,ListView 是一个非常常用的控件,类似于 HTML 中的 ul 或者 ol

dart复制代码body: ListView(
  children: [
    Container(
      height: 100,
      color: Colors.red,
      child: Center(child: Text("第一个Item")),
    ),
    Container(
      height: 100,
      color: Colors.blue,
      child: Center(child: Text("第二个Item")),
    ),
    // 更多的Item
  ],
)
4. ExpandedFlexible

当你想要让某些子组件根据父组件的剩余空间自动调整大小时,ExpandedFlexible 是非常有用的。

dart复制代码body: Row(
  children: [
    Container(width: 100, height: 100, color: Colors.red),
    Expanded(
      child: Container(
        height: 100,
        color: Colors.blue,
        child: Text("我会填满剩下的空间"),
      ),
    ),
  ],
)

注意

往往使用这个expanded来进行达到flex布局的效果时候 (只看内容部分 ) ,如果是Row的属性 flex 比列就是行宽占比 ,Colum 就是列

void main() {

  /**
   * aterialApp 是 Flutter 框架中的一个顶级组件,用于构建一个基于 Material Design 风格的应用程序。Material Design 是 Google 推出的设计规范,强调简洁、响应式、多平台的一致用户体验,Flutter 提供了 MaterialApp 这个类来帮助开发者快速构建符合 Material Design 的应用。

      MaterialApp 作用
      MaterialApp 是 Flutter 应用的入口,负责管理应用的路由、主题、导航、以及一些全局的设置。它类似于 Android 中的 Application 类或是 iOS 中的 AppDelegate。
      为app 开发提供了模板 在其中写的组件 除开防线组件不需要写模板
   */

//1. 定义 TextTheme
  TextTheme textTheme = const TextTheme(
    bodyLarge:  TextStyle(fontSize: 18, color: Colors.red),
    // 可以定义更多的文本样式...
  );

    runApp(MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.blue,
//      2.  全局使用
        textTheme:textTheme),

      //为 Material Design 微件创建可视化基架。
      home: Scaffold(
        appBar: AppBar(
          //左边的图标
          leading: const Icon(Icons.access_alarms),
          // 中间区域 title
          title: const Center(child:   Text("测试app主页",
                style: TextStyle(
                  shadows:[],
                  color: Colors.cyan,
                )),
          ),
//          右边区域
          actions:[
            //3.  全局使用
            Text("详细查看", style: textTheme.bodyLarge, ),
            const Icon(Icons.ac_unit_sharp),
          ],
        ),

        // 内容部分

          body: Column(
            children: [
              // 使用 Expanded 来占据屏幕宽度的剩余空间
              Expanded(
                flex: 2, // 表示占据的比例,值越大占据的空间越多
                child: Container(
                  color: Colors.red,
                  child: Center(child: Text("红色容器")),
                ),
              ),
              Expanded(
                flex: 1, // 这里的比例是1:1:1

                child: Container(
                  color: Colors.blue,
                  child: Center(child: Text("蓝色容器")),
                ),
              ),
              Expanded(
                flex: 1, // 这里的比例是1:1:1
                child: Container(
                  color: Colors.green,
                  child: Center(child: Text("绿色容器")),
                ),
              ),
            ],
          ),




          //浮动按钮 可以点击 点击时触发事件 容器预一营好的布局 改按钮是在容器中 右下角
        floatingActionButton: FloatingActionButton(
          child: const Icon(Icons.add),
        onPressed: () {
          print("点击了按钮");
        },
        tooltip: "长按触发事件",
        ),

        //底部的导航栏 有items 属性 当前位置 索引 点击时时间等
        bottomNavigationBar: buildBtoomnNavigationBar()
      ),
    ));
}

示例:实际应用中的 body 布局

body: Column(
  children: [
    Container(
      height: 200,
      color: Colors.blue,
      child: Center(child: Text("顶部区域")),
    ),
    Expanded(
      child: ListView(
        children: [
          Container(
            height: 100,
            color: Colors.red,
            child: Center(child: Text("第一个Item")),
          ),
          Container(
            height: 100,
            color: Colors.green,
            child: Center(child: Text("第二个Item")),
          ),
        ],
      ),
    ),
    Container(
      height: 100,
      color: Colors.yellow,
      child: Center(child: Text("底部区域")),
    ),
  ],
)

动态布局

在 Flutter 中,Expanded 是一种非常常用的控件,它允许子控件在父容器中根据可用空间进行扩展,以实现响应式布局。但并不是只有 Expanded 可以实现响应式布局,Flutter 提供了多种方式实现响应式布局,取决于具体的需求和场景。下面是几种实现响应式布局的常见方法:

ExpandedFlexible

  • Expanded:可以让子控件在父容器中占据剩余的可用空间。所有使用 Expanded 的子控件都会均匀分配可用空间。

    示例:

    Row(
      children: [
        Expanded(
          child: Container(color: Colors.red),
        ),
        Expanded(
          child: Container(color: Colors.green),
        ),
      ],
    )
    

    在这个例子中,两个 Container 会各自占据一半的可用空间。

  • Flexible:与 Expanded 类似,但 Flexible 可以让子控件在父容器中根据比例分配空间,且子控件不会强制填满空间。

    示例:

    Row(
      children: [
        Flexible(
          flex: 2,
          child: Container(color: Colors.red),
        ),
        Flexible(
          flex: 1,
          child: Container(color: Colors.green),
        ),
      ],
    )
    

    这里,Container 会按照 2:1 的比例来分配剩余空间。

MediaQuery

MediaQuery 是 Flutter 中用于获取屏幕尺寸和设备信息的工具。你可以使用它来根据屏幕的大小调整布局,从而实现响应式设计。

示例:

double screenWidth = MediaQuery.of(context).size.width;

Container(
  width: screenWidth * 0.5,  // 设置容器宽度为屏幕宽度的50%
  height: 100,
  color: Colors.blue,
)

在这个例子中,我们根据屏幕的宽度调整了 Container 的大小,从而实现了响应式布局。

LayoutBuilder

LayoutBuilder 允许你根据父容器的尺寸来动态调整子控件的布局。它特别适合处理不同尺寸的布局需求。

示例:

LayoutBuilder(
  builder: (context, constraints) {
    if (constraints.maxWidth > 600) {
      return Text("大屏布局");
    } else {
      return Text("小屏布局");
    }
  }
)

在这个例子中,根据父容器的宽度,Text 的内容会发生变化。

AspectRatio

AspectRatio 控件可以帮助你控制子控件的宽高比,确保其响应式调整大小。

示例:

AspectRatio(
  aspectRatio: 16/9,
  child: Container(color: Colors.red),
)

这个 AspectRatio 控件会确保 Container 以 16:9 的比例进行缩放,无论父容器的大小如何。

FittedBox

FittedBox 用于调整子控件的大小以适应父容器,并保持子控件内容的比例。

示例:

FittedBox(
  child: Text('这是一个长文本'),
)

FittedBox 会自动缩放 Text,以确保它能够适应可用的空间。

Wrap

Wrap 是一种类似 RowColumn 的布局方式,但它允许子控件在空间不足时换行,因此在处理动态数量的子控件时非常有用。

示例:

Wrap(
  children: [
    Container(width: 100, height: 100, color: Colors.red),
    Container(width: 100, height: 100, color: Colors.green),
    Container(width: 100, height: 100, color: Colors.blue),
    Container(width: 100, height: 100, color: Colors.yellow),
  ],
)

在这个例子中,Wrap 会在一行容不下所有 Container 时自动换行,从而实现响应式布局。

分割符

Spacer(),

用来做容器中的内容分割,点击源码 发现默认是一个一个占比的 所有整个 容器的占比 就至少根据这个情况来

  const Spacer({super.key, this.flex = 1})
    : assert(flex > 0);

比如

          body: Column(
            children: [
              Expanded(
                flex: 20, // 表示占据的比例
                child: Container(
                  color: Colors.red,
                  child: Center(
                    child: Column(  // 使用 Column 允许在同一区域放置多个控件
                      mainAxisAlignment: MainAxisAlignment.center,  // 垂直居中对齐
                      children: [
                        Container(
                          width: 100,
                          height: 100,
                          color: Colors.amberAccent,
                          child: TextButton(
                            onPressed: () => print("用户点击了按钮"),
                            onLongPress: () => print("长按触发事件"),
                            child: const Text("点击即可"),
                          ),
                        ),
                        const SizedBox(height: 20),  // 增加一个空隙
                        Container(
                          width: 100,
                          height: 100,
                          color: Colors.lightBlue,
                          child: TextButton(
                            onPressed: () => print("用户点击了另一个按钮"),
                            child: const Text("另一个按钮"),
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
              ),
//              占位分割符
              Spacer(),
              Expanded(
                flex: 10,
                child: Container(
                  color: Colors.blue,
                  child: Center(child: Text("蓝色容器")),
                ),
              ),
              Spacer(),
              Expanded(
                flex: 10,
                child: Container(
                  color: Colors.green,
                  child: Center(child: Text("绿色容器")),
                ),
              ),
            ],
          ),

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

用户交互:手势检测

手势控件(如 GestureDetectorInkWell 等)主要是用于捕捉和处理用户的手势操作,它们通常应用于需要处理交互的局部区域,而不是全局应用。因此,它们不需要放在全局的 MaterialApp 中,而是用于包裹具体的 UI 组件(如按钮、图片、容器等),以检测用户在这些组件上的手势操作。

   Expanded(
                flex: 20,
                child: GestureDetector(
                  // 单击
                  onTap: () {
                    print("监听点击了按钮");
                  },
                  // 双击
                  onDoubleTap: () {
                    print("监听该区域双击了按钮");
                  },
                  // 长按
                  onLongPress: () {
                    print("监听长按触发事件");
                  },
                  // 长按抬起
                  onLongPressUp: () {
                    print("监听长按后松手");
                  },
                  // 按下
                  onTapDown: (details) {
                    print("监听手指按下");
                  },
                  // 点击抬起
                  onTapUp: (details) {
                    print("监听手指松开");
                  },
                  // 点击取消
                  onTapCancel: () {
                    print("监听点击取消");
                  },
                  onPanStart: (details) {
                    print("监听任意方向拖动开始");
                  },
                  onPanUpdate: (details) {
                    print("监听任意方向拖动更新");
                  },
                  onPanEnd: (details) {
                    print("监听任意方向拖动结束");
                  },


                  // 垂直拖动更新
                  // onVerticalDragUpdate: (details) {
                  //   print("监听垂直拖动更新");
                  // },
                  // 垂直拖动结束
                  // onVerticalDragEnd: (details) {
                  //   print("监听垂直拖动结束");
                  // },
                  // 缩放开始
                  // onScaleStart: (details) {
                  //   print("监听缩放开始");
                  // },
                  // 缩放更新
                  // onScaleUpdate: (details) {
                  //   print("监听缩放更新");
                  // },
                  // 缩放结束
                  // onScaleEnd: (details) {
                  //   print("监听缩放结束");
                  // }, 
                      	//监听的区域
                  child: Container(
                    color: Colors.amber,
                    child: Container(
                      color: Colors.blue,
                      child:const Center(child: Text("蓝色容器")),
                    ),
                  ),
                ),
              ),
             

注意:

同时设置了水平拖动 (onHorizontalDrag)、垂直拖动 (onVerticalDrag) 和缩放手势 (onScale)。这三种手势在一起时会冲突,因为它们都涉及拖动,导致缩放手势被忽略。

其他交互相关: 交互性 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter

页面响应式–有状态控件

和react 和vue 一样,可以根据携带状态的改变刷新页面,对应react的usestatte,vue的响应式ref,也可以全局状态管理pinia等状态管理工具

  • 有状态的控件指的是在 Flutter 中可以通过修改其内部状态来响应用户交互或其他事件的控件。
    这些控件可以是按钮、文本框、复选框、单选按钮等,
  • 它们在内部都有自己的状态,例如按钮的选中状态、文本框的输入内容等。

之前使用的class Text extends StatelessWidget 基本都是继承无状态控件

有状态控件 可以把无状态控制包裹起来实现动态渲染

使用步骤

1.自定义控件继承 StatefulWidget ,重写起创建方法createState.(调用构造函数时候默认调用)

2.自定义状态包含初始状态,并且继承State<自定义控件> 重写起build 方法(一个返回控件的初始化方法)

3.此时自定义内的数据(状态是收到监听的,当使用setState方法的时候 ,重新渲染控件build 方法)从而实现更新

/**
 * 有状态的控件指的是在 Flutter 中可以通过修改其内部状态来响应用户交互或其他事件的控件。
 这些控件可以是按钮、文本框、复选框、单选按钮等,
 * 它们在内部都有自己的状态,例如按钮的选中状态、文本框的输入内容等。
 */

import 'package:flutter/material.dart';

/**
 * 1.继承自 StatefulWidget 的有状态控件通常需要实现一个 State 类来管理其内部状态。
 */
class CounterWidget extends StatefulWidget {
  
  State<StatefulWidget> createState() {
    // TODO: implement createState

    return CountState();
  }

}

// 在使用有状态控件时,通常需要在 State 类中定义一些方法来处理用户交互或其他事件,例如点击按钮时增加计数器的值。
class CountState extends State<CounterWidget> {
  int _counter = 0;//state
 // 2.重写build方法 每次页面刷新 时候都会执行一下 build方法
  
  Widget build(BuildContext context) {
    print("页面刷新"+DateTime.now().toString());

    return Scaffold(
      appBar: AppBar(title: Text('有状态控件示例')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('按钮被按下了这么多次:'),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      // 浮动按钮
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,//调用自增action 改变了state 后 state数据改变,会重新执行build 方法进行渲染更新新数据到视图上,
        tooltip: '自增按钮',
        child: Icon(Icons.add),
      ),
    );
  }
    //action
  // 更新状态
  void _incrementCounter() {
      //调用setState方法后 页面会重新执行build方法进行渲染
    setState(() {
      _counter++;
    });
  }
  // 清理资源
  
  void dispose() {
    print('控件被销毁');
    super.dispose();
  }


}

void main() {
    //使用构造函数返回携带状态的视图
  runApp(MaterialApp(home: CounterWidget()));
}
/**

}

**/

setState方法是状态控件的父类方法

android studio快捷健 stful

全局状态

Provider 是 Flutter 中的一种状态管理方式,它基于 InheritedWidget,但比直接使用 InheritedWidget 更简洁和强大。Provider 通常用于全局管理状态、共享数据,以及在不同的组件之间进行数据传递。并且由于flutter是嵌套组件进行构建app,如果子孙组件,因为父类选择的item,需要刷新,但是父级组件状态改变,然后嵌套深的无关子孙组件也要跟着重新渲染,这样就造成了性能损失,所以下面我将详细解释 Provider 的使用。

1. 引入 Provider

在开始使用 Provider 之前,需要将它添加到项目的 pubspec.yaml 文件中:

dependencies:
   provider: ^6.1.2

然后在 Dart 文件中引入 provider 包:

import 'package:provider/provider.dart';

2. 基本概念

  • ChangeNotifier:这是一个可监听的类,用于保存状态。通过调用 notifyListeners() 方法,可以通知所有监听者更新数据。
  • ChangeNotifierProvider:这是 Provider 提供的一种具体实现,它与 ChangeNotifier 搭配使用,能够提供状态和监听变化。
  • Consumer:它是用来读取 Provider 中的状态并构建 UI 的小部件。它能够订阅某个状态,并在状态发生变化时自动重建 UI。

3. ChangeNotifierChangeNotifierProvider

ChangeNotifier 是一个简化的状态管理类。可以创建一个 ChangeNotifier 类来管理应用的状态:

示例:计数器的状态管理

  1. 创建 ChangeNotifier
//类似dart 对多集成的实现
class Counter with ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners(); // 通知所有监听这个状态的Widget
  }
}
  1. 在应用中提供 ChangeNotifierProvider

在根组件(或任意父组件)中使用 ChangeNotifierProvider 来提供 Counter 状态:

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => Counter(), // 创建并提供Counter实例
      child: MyApp(),
    ),
  );
}
  1. 在子组件中使用 ConsumerProvider.of 获取状态

可以通过 ConsumerProvider.of 来监听状态,并在状态发生变化时更新 UI。

  • 使用 Consumer
class CounterScreen extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Counter App'),
      ),
      body: Center(
          //局部渲染的组件 使用conumer 包裹
        child: Consumer<Counter>(
          builder: (context, counter, child) {
            return Text(
              'Count: ${counter.count}', // 获取计数状态
              style: TextStyle(fontSize: 40),
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
            //由于在顶级组件树注入的状态,所以偶可以使用上下文read来进行读取
          context.read<Counter>().increment(); // 修改计数状态
        },
        child: Icon(Icons.add),
      ),
    );
  }
}
  • 使用 Provider.of
class CounterScreen extends StatelessWidget {
  
  Widget build(BuildContext context) {
    final counter = Provider.of<Counter>(context);
    return Scaffold(
      appBar: AppBar(
        title: Text('Counter App'),
      ),
      body: Center(
        child: Text(
          'Count: ${counter.count}', // 获取计数状态
          style: TextStyle(fontSize: 40),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          counter.increment(); // 修改计数状态
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

4. 多个 Provider 的使用

有时应用需要管理多个状态。可以通过 MultiProvider 来提供多个 Provider

示例:多个状态管理

  1. 创建多个 ChangeNotifier
class Counter with ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

class Message with ChangeNotifier {
  String _message = "Hello";

  String get message => _message;

  void changeMessage(String newMessage) {
    _message = newMessage;
    notifyListeners();
  }
}
  1. 使用 MultiProvider
void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) => Counter()),
        ChangeNotifierProvider(create: (context) => Message()),
      ],
      child: MyApp(),
    ),
  );
}
  1. 在子组件中获取不同的状态
class MultipleStateScreen extends StatelessWidget {
  
  Widget build(BuildContext context) {
    final counter = Provider.of<Counter>(context);
    final message = Provider.of<Message>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('Multi Provider Example'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text(
            'Count: ${counter.count}',
            style: TextStyle(fontSize: 40),
          ),
          Text(
            'Message: ${message.message}',
            style: TextStyle(fontSize: 20),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          counter.increment();
          message.changeMessage("New Message");
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

5. Provider.ofConsumerSelector 的区别

  • Provider.of:立即获取 Provider 中的值。如果 listentrue(默认值),则当值发生变化时,组件会重建。
  • Consumer:专门用于监听 Provider 中的数据变化,并且只会更新 Consumer 里面的部分组件,这可以避免不必要的组件重建。
  • Selector:可以选择监听 Provider 中的某个部分数据变化,避免整个对象变化时导致不必要的重建。

使用 Selector

class CounterScreen extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Selector Example'),
      ),
      body: Center(
        child: Selector<Counter, int>(
          selector: (context, counter) => counter.count, // 只监听 count 的变化
          builder: (context, count, child) {
            return Text(
              'Count: $count',
              style: TextStyle(fontSize: 40),
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          context.read<Counter>().increment();
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

6. 总结

Provider 是一种简便且高效的状态管理方式,和react 一样将全局状态在顶级组件树木注入,让子组件上下文可以直接使用读取,并且可以使用conmuer包裹监听渲染特定部分,状态改变时候,之刷新comsumer 包裹部分

尤其适用于较小的 Flutter 应用或状态较简单的情况。通过使用 ChangeNotifierChangeNotifierProvider,可以轻松实现状态的全局管理。同时,通过 ConsumerSelector 等机制,能够精准控制组件的重建,从而提升应用性能。

无状态控件

快捷健stless

main(){
  runApp(MaterialApp(home: myState()));
}
class myState extends StatelessWidget {
  const myState({super.key});

  
  Widget build(BuildContext context) {
    int i = 100;
    print("自定义无状态组件初始化");
    return Placeholder(child: Column(
        children: <Widget>[
          ElevatedButton(onPressed: ()=>{
            i++,
            print("$i"),
          }, child: Text("点击输出当前控件的数值")),
        ],
      ),
    );
  }
}

点击屏幕 发现控件内的数据i虽然变化了,但是页面不会渲染也就是所谓的无状态,不会随着状态改变


生命周期

只有有状态控件才会有生命周期,并且flutter的生命周期感觉和react还有一些相似

都是挂载组件/控件树

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在Flutter中,生命周期的管理类似于React,尤其是在组件的创建、更新和销毁过程中,都会触发不同的生命周期方法。在你的Flutter代码中,你定义了一个StatefulWidget,因为只有有状态的组件才有生命周期方法。接下来我会详细介绍这些生命周期方法并补充完整。

Flutter 生命周期方法解析:

  1. createState()

    • MyHome这个StatefulWidget中,createState()方法是首先被调用的,用于创建与该组件关联的状态对象_MyHomeState
    • 打印输出:“1.初始化控件 执行了createState方法”
  2. initState()

    • initState()State类的第一个生命周期方法,它在状态对象被创建时调用。通常用于组件的初始化操作,比如获取数据、订阅事件等。
    • 你在initState()中进行了网络图片的初始化加载,这种做法类似于React中的componentDidMount()
    • 打印输出:“2.执行了state的initState方法”
    • 注意:必须调用super.initState(),以确保父类的初始化逻辑也被执行。
  3. build()

    • build() 方法在组件每次需要更新UI时被调用。这就类似于React中的render()方法。Flutter会根据你在setState()中触发的状态变更,调用build()重新渲染UI。
    • 每次点击按钮后调用setState(),Flutter会再次调用build()方法。
    • 打印输出:“3.执行了build方法 渲染控件”
  4. didUpdateWidget() (可选):

    • StatefulWidget的配置发生变化时,didUpdateWidget() 会被调用。可以通过这个方法来处理父组件传递给子组件的新数据。类似于React的componentDidUpdate()
    • 如果你修改父组件的数据传递给子组件,比如你在外部改变了MyHome的某些参数,Flutter会调用这个方法。

    示例

    
    void didUpdateWidget(covariant MyHome oldWidget) {
      super.didUpdateWidget(oldWidget);
      print("4.执行了didUpdateWidget方法");
    }
    
  5. dispose()

    • 当组件不再需要时,dispose() 会被调用,用来释放资源,比如取消网络请求、监听器、动画等。
    • 你在代码中正确地使用了dispose()来释放组件。
    • 打印输出:“释放资源控件销毁”
  6. deactivate() (可选):

    • deactivate() 方法在组件从树中被移除时调用,通常用于从父组件或其他依赖的地方解除绑定。这和dispose()不同,dispose()是在彻底销毁前调用,而deactivate()在组件可能还会重新插入树中时调用。

    示例

    
    void deactivate() {
      super.deactivate();
      print("5.执行了deactivate方法");
    }
    
  7. reassemble() (可选):

    • 该方法主要用于热重载(hot reload)期间,开发时重新编译代码后,reassemble() 会被调用。正常情况下应用不会用到这个方法。

    示例

    
    void reassemble() {
      super.reassemble();
      print("执行了reassemble方法");
    }
    

完整的生命周期顺序:

  • createState()
  • initState()
  • build()
  • didUpdateWidget()(在组件重新构建时,如果父组件传入新参数)
  • deactivate()(组件从树中被移除)
  • dispose()(释放资源,销毁组件)

生命周期优化:

Flutter与React相似,使用setState()来更新UI,触发build()的重新渲染。为了避免不必要的重绘,通常需要谨慎使用setState(),确保它只在需要更新的地方调用,类似于React中的shouldComponentUpdate。可以使用全局状态插件provider

void main() {
  runApp(MaterialApp(
      home: Scaffold(
          appBar: AppBar(
            title: Text('演示控件的生命周期'),
          ),
          body: MyHome())));
}

class MyHome extends StatefulWidget {
  int a = 10;
  MyHome({super.key});

  
  State<MyHome> createState() {
    print("1.初始化控件 执行了createState方法");
    return _MyHomeState();
  }
}

class _MyHomeState extends State<MyHome> {
  List<String> images = [];
  int index = 0;

  // 2. initState: 初始化状态
  
  void initState() {
    print("2.执行了state的initState方法");
    images.addAll([
      'https://img.zcool.cn/community/017f51563447666ac7259e0f1522ea.jpg@1280w_1l_2o_100sh.jpg',
      "https://img.zcool.cn/community/01129957723f4b0000018c1b6692bb.jpg@2o.jpg",
      "https://n.sinaimg.cn/sinacn10113/332/w1024h1708/20190806/3afd-iatixpm8624881.jpg",
      "http://image.yjcf360.com/u/cms/www/201905/25084330vx4w.jpg"
    ]);
    super.initState();
  }

  // 3. didChangeDependencies: 当依赖的对象发生变化时调用(比如 InheritedWidget)
  
  void didChangeDependencies() {
    super.didChangeDependencies();
    print("3.执行了didChangeDependencies方法");
  }

  // 4. build: 构建UI界面
  
  Widget build(BuildContext context) {
    print("4.执行了build方法 渲染控件");
    return Column(
      children: [
        Expanded(
          flex: 80,

          child: Image.network(
            // 宽度充满父容器 高度自适应 这个参数标识无穷大
            width: double.infinity,
            // 加载网络图片
            images[index],
            fit: BoxFit.cover,
          ),
        ),
        Expanded(
          flex: 20,
          child: Center(
              child: ElevatedButton(
                  style: ButtonStyle(
                    side: MaterialStateProperty.all<BorderSide>(BorderSide(
                      color: Colors.black,
                      width: 2.0,
                    )),
                  ),
                  onPressed: () => {
                    setState(() {
                      index = (index + 1) % images.length;
                    })
                  },
                  child: Text("点击切换图片"))),
        )
      ],
    );
  }

  // 5. didUpdateWidget: 当组件状态改变时(比如父组件传递新的数据),调用此方法
  
  void didUpdateWidget(covariant MyHome oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("5.执行了didUpdateWidget方法");
  }

  // 6. reassemble: 热重载时会调用(开发调试用)
  
  void reassemble() {
    super.reassemble();
    print("6.执行了reassemble方法(热重载时调用)");
  }

  // 7. deactivate: 当组件从树中移除时调用(还没被销毁)
  
  void deactivate() {
    super.deactivate();
    print("7.执行了deactivate方法");
  }

  // 8. dispose: 销毁组件时调用,释放资源
  
  void dispose() {
    print("8.释放资源 执行了dispose方法");
    super.dispose();
  }
}

滑动布局

当行列布局如果超出容器后 会出现话花屏的感觉 比如 我某个区间容器是row 布局 但是·是多个文本 ,当数据过长时候 发现竟然不会自动换行 而是渲染花屏

main(){
  runApp(MaterialApp(
    home: MyrowLogout(),
  ));
}
class MyrowLogout  extends StatefulWidget {
  const MyrowLogout ({super.key});

  
  State<MyrowLogout > createState() => _MyrowLogoutState();
}

class _MyrowLogoutState extends State<MyrowLogout > {
  
  Widget build(BuildContext context) {
    return Row(
      children: [
        //此时  文本内容  超出屏幕后 右边会显示花屏
        Text("我是一条超级长显示的文本内容,哈哈哈哈你好")
      ],
    );
  }
}

在这里插入图片描述

当你的文本内容超出一行的宽度时,Row 布局不会自动换行,这也是为什么你会看到渲染失败的黄屏。Row 是一个水平布局控件,默认情况下,它会尝试将所有的子控件在同一行上显示,如果内容超出可用宽度,就会出现布局溢出问题。

要解决这个问题,可以考虑以下几种方法:

**使用 ExpandedFlexible**等动态布局

你可以使用 ExpandedFlexible 控件包裹row布局的子控件 Text,让文本根据屏幕的宽度自动换行并避免超出布局。它们会自动占用 Row 的可用空间,并将文本换行。

main(){
  runApp(MaterialApp(
    home: MyrowLogout(),
  ));
}

class MyrowLogout extends StatefulWidget {
  const MyrowLogout({super.key});

  
  State<MyrowLogout> createState() => _MyrowLogoutState();
}

class _MyrowLogoutState extends State<MyrowLogout> {
  
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Expanded( // 使用 Expanded 包裹 Text 控件
          child: Text(
            "我是一条超级长显示的文本内容,哈哈哈哈你好",
            style: TextStyle(fontSize: 10),
          ),
        ),
        Expanded(
          child: Text("我是一条超级长显示的文本内容,哈哈哈哈你好"),
        ),
        Expanded(
          child: Text("我是一条超级长显示的文本内容,哈哈哈哈你好"),
        ),
      ],
    );
  }
}

布局依旧是row 对于布局的每个子容器 如果出现无法装载的情况 就会换行

在这里插入图片描述

使用 Wrap 控件

Wrap 是一个专门用于自动换行的控件,它会自动将子控件换到下一行或下一列,以避免布局溢出。对于这种情况,使用 Wrap 来代替 Row 可以很好地解决问题。

main(){
  runApp(MaterialApp(
    home: MyrowLogout(),
  ));
}

class MyrowLogout extends StatefulWidget {
  const MyrowLogout({super.key});

  
  State<MyrowLogout> createState() => _MyrowLogoutState();
}

class _MyrowLogoutState extends State<MyrowLogout> {
  
  Widget build(BuildContext context) {
    return Wrap(
      // Wrap 会自动换行,当内容超出时
      children: [
        Text(
          "我是一条超级长显示的文本内容,哈哈哈哈你好",
          style: TextStyle(fontSize: 10),
        ),
        Text("我是一条超级长显示的文本内容,哈哈哈哈你好"),
        Text("我是一条超级长显示的文本内容,哈哈哈哈你好"),
      ],
    );
  }
}

这种效果类似col布局了

在这里插入图片描述

使用 SingleChildScrollView 控件

如果你希望保留 Row 布局,并让超出部分可以水平滚动,而不是换行,可以使用 SingleChildScrollView 来包裹 Row,实现水平滚动。

main(){
  runApp(MaterialApp(
    home: MyrowLogout(),
  ));
}

class MyrowLogout extends StatefulWidget {
  const MyrowLogout({super.key});

  
  State<MyrowLogout> createState() => _MyrowLogoutState();
}

class _MyrowLogoutState extends State<MyrowLogout> {
  
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      scrollDirection: Axis.horizontal, // 水平滚动
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(
            "我是一条超级长显示的文本内容,哈哈哈哈你好",
            style: TextStyle(fontSize: 10),
          ),
          Text("我是一条超级长显示的文本内容,哈哈哈哈你好"),
          Text("我是一条超级长显示的文本内容,哈哈哈哈你好"),
        ],
      ),
    );
  }
}

  • ExpandedFlexible:让文本根据可用空间自动换行,适合你希望内容按比例分配空间的情况。
  • Wrap:适合需要自动换行的场景,不再局限于一行显示。
  • SingleChildScrollView:适合需要水平滚动的场景。

根据你的需求选择合适的布局控件即可避免渲染失败的黄屏问题。

在 Flutter 中,实现滑动区域的内容时,有多种可滑动的视图组件,每个组件在不同的场景下适用,尤其是像你提到的图文应用(如小红书)中,涉及到图片、文本内容的滑动。以下是一些常用的可滑动视图组件及它们的区别:

SingleChildScrollView 适用于内容较少的情况,允许在一个方向上(水平或垂直)滚动其子元素。

  • 适用场景

    • 需要将所有内容包裹在一个滚动区域里。
    • 内容较小,可以一次性加载完成,不需要懒加载或分页加载。
  • 缺点

    • 不能在需要大量数据滚动时使用,因为它不会在视图外部丢弃不可见的子元素,可能会导致性能问题。

ListView(动态页面推荐)

ListView 是最常用的滚动组件之一,适合垂直方向上大量的可滚动内容,通常用于显示列表数据。

  • 适用场景

    • 适用于垂直方向的长列表内容,比如新闻列表、图片集等。
    • 支持懒加载,即只渲染当前视口内的元素,不会加载所有内容,性能表现优越。
    • 支持无限滚动和分段加载。
  • 种类

    • ListView.builder():适用于大量数据,动态创建视图。
    • ListView.separated():用于在列表项之间添加分割线。
    • ListView.custom():高度自定义列表的行为。
  • 示例

    静态

    ListView(
      padding: const EdgeInsets.all(8),
      children: <Widget>[
        Container(
          height: 50,
          color: Colors.amber[600],
          child: const Center(child: Text('Entry A')),
        ),
        Container(
          height: 50,
          color: Colors.amber[500],
          child: const Center(child: Text('Entry B')),
        ),
        Container(
          height: 50,
          color: Colors.amber[100],
          child: const Center(child: Text('Entry C')),
        ),
      ],
    )
    

    动态(懒加载数据 视图到哪里才加载哪里)

          ListView.builder(
                      itemCount: 1000,  // 列表项的数量
                      itemBuilder: (context, index) {
                        return  Container(
                          height: 50,
                          color: Colors.amber[500],
                          child: Center(child: Text(' 区域 ')),
                        );
                      },
                    )
    

GridView

GridView 适用于以网格形式显示内容,比如图片墙、商品展示等。

  • 适用场景

    • 适合在横向和纵向上都需要滑动的网格布局场景,如图片展示、商品卡片。
    • 支持懒加载,仅渲染当前视图内的元素。
  • 种类

    • GridView.builder():动态构建网格项,适合大量数据。
    • GridView.count():预设固定数量的列。
    • GridView.custom():自定义网格行为。
  • 示例

    GridView.builder(
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 2, // 每行两列
      ),
      itemCount: 20,
      itemBuilder: (context, index) {
        return Card(
          child: Column(
            children: [
              Image.network('https://example.com/image.jpg'),
              Text('商品 $index'),
            ],
          ),
        );
      },
    )
    

PageView (实现轮播图)

PageView 用于水平或垂直方向的分页滑动视图,比如显示类似于轮播图、教程页面的效果。

  • 适用场景

    • 用于展示分屏效果,比如分页显示内容、图片轮播。
    • 可以自定义滑动动画、页面指示器。
  • 示例

    PageView(
      children: [
        Image.network('https://example.com/image1.jpg'),
        Image.network('https://example.com/image2.jpg'),
        Image.network('https://example.com/image3.jpg'),
      ],
    )
    

**CustomScrollView + Slivers ** (推荐)

CustomScrollView 是 Flutter 中非常灵活的可滚动区域,用于构建复杂的滚动效果。Sliver 是构建这种滚动区域的基础,它允许创建灵活的自定义布局,比如带有吸顶效果的 SliverAppBar 或自定义的滚动动画。

  • 适用场景

    • 适用于复杂的滚动效果,比如头部吸附、分段显示列表、网格与列表组合等。
    • 可以组合不同类型的 Sliver,如 SliverListSliverGridSliverAppBar
    • 适用于构建如小红书这样复杂的 UI 布局。
  • 示例

    CustomScrollView(
      slivers: [
        SliverAppBar(
          expandedHeight: 200.0,
          flexibleSpace: FlexibleSpaceBar(
            title: Text('小红书风格页面'),
            background: Image.network('https://example.com/banner.jpg', fit: BoxFit.cover),
          ),
        ),
        SliverList(
          delegate: SliverChildBuilderDelegate(
            (BuildContext context, int index) {
              return ListTile(
                title: Text('Item $index'),
              );
            },
            childCount: 50,
          ),
        ),
      ],
    )
    

NestedScrollView

NestedScrollView 允许在一个页面中同时包含两个滚动视图,通常用于顶部有 SliverAppBar 并且页面内部还有可以滚动的内容,比如一个列表或网格。

  • 适用场景

    • 适用于需要同时滚动头部(如 AppBar)和内容(如列表或网格)的场景。
    • 实现类似于下拉刷新的效果,或头部滑动隐藏与显示效果。
  • 示例

    NestedScrollView(
      headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
        return <Widget>[
          SliverAppBar(
            expandedHeight: 200.0,
            floating: false,
            pinned: true,
            flexibleSpace: FlexibleSpaceBar(
              title: Text("NestedScrollView Demo"),
            ),
          ),
        ];
      },
      body: ListView.builder(
        itemCount: 50,
        itemBuilder: (BuildContext context, int index) {
          return ListTile(
            title: Text('Item $index'),
          );
        },
      ),
    )
    

Scrollable

Scrollable 是最基础的滚动组件,几乎所有其他滚动组件(如 ListViewGridView)都基于它构建。一般情况下,开发者不会直接使用 Scrollable,而是使用封装好的组件。

  • 适用场景

    • 在需要高度自定义滚动行为时使用。
  • 示例

    Scrollable(
      axisDirection: AxisDirection.down,
      viewportBuilder: (context, position) {
        return Viewport(
          offset: position,
          slivers: [
            SliverToBoxAdapter(
              child: Text('这是一个自定义滚动视图'),
            ),
          ],
        );
      },
    )
    

结论

  • 单一页面滑动:如果页面内容较少或需要垂直滚动,SingleChildScrollView 是一个简单的选择,但当内容量大时,建议使用 ListViewGridView
  • 分页滑动PageView 适合实现类似轮播图或教程分屏的效果。
  • 复杂布局:如果你需要构建类似于小红书的复杂页面布局,可以考虑使用 CustomScrollView,结合 SliverListSliverGrid 等构建高度灵活的滑动内容。
  • 头部和内容同时滚动NestedScrollView 适合在页面中既有滚动头部,又有内容滚动的情况。

根据你的需求,选择最合适的组件可以提升开发效率和用户体验。

路由跳转

Navigator

Navigator 是一个全局访问点,用于推送和弹出路由。它是 Flutter 中管理页面导航的中央对象。

和vue,react一样,flutter的路由也是路由栈,一个页面一个页面的往上叠

  • Navigator.push()
    这个方法会将一个新的路由添加到路由栈的顶部,并触发页面转换动画。它返回一个 Future,该 Future 在新页面通过 Navigator.pop 被弹出时完成。

    Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => NewScreen()),
    ).then((value) {
      // 新页面通过 Navigator.pop 返回的结果
    });
    
    
    
  • Navigator.pop()
    这个方法用于从路由栈中移除当前路由,并返回到前一个路由。如果提供了参数,它将作为结果返回给前一个页面。

    Navigator.pop(context, result);
    
  • Navigator.popUntil()
    这个方法弹出当前路由,直到遇到符合给定条件的路由。这允许你一次性弹出多个路由。

    Navigator.popUntil(context, (route) {
      return route.settings.name == 'target_route_name';
    });
    
  • Navigator.canPop()
    这个方法返回一个布尔值,指示当前路由是否可以被弹出。

    final canPop = Navigator.canPop(context);
    

Route

Route 是表示导航路径的基类。它定义了路由的基本接口,包括 buildContent(), popped(), willShow(), didShow() 等方法。

  • PageRoute
    PageRouteRoute 的具体实现,它表示一个全屏页面,并提供了页面转换动画。PageRoute 需要被实现以创建自定义的路由。

    class MyCustomRoute extends PageRoute {
      
      Widget buildContent(BuildContext context) {
        return NewScreen();
      }
    
      
      Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
        // 自定义过渡动画
        return FadeTransition(opacity: animation, child: child);
      }
    }
    
  • MaterialPageRoute
    MaterialPageRoutePageRoute 的具体实现,它提供了 Material Design 风格的过渡动画。

    MaterialPageRoute(
      builder: (context) => NewScreen(),
      settings: RouteSettings(name: 'new_screen_route'),
    )
    

PageRouteBuilder

PageRouteBuilder 允许你以编程方式构建路由,而不是在 MaterialApproutes 字典中静态定义。

Navigator.push(
  context,
  PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) => NewScreen(),
    transitionsBuilder: (context, animation, secondaryAnimation, child) {
      return FadeTransition(
        opacity: animation,
        child: child,
      );
    },
  ),
);

Navigator.restorablePush() 方法

这个方法类似于 Navigator.push(),但它允许你保存和恢复路由状态。

Navigator.restorablePush(
  context,
  MaterialPageRoute(builder: (context) => NewScreen()),
);

ModalRoute

ModalRoutePageRoute 的子类,表示一个模态路由。它提供了当前路由的状态和信息,如 isCurrent, isFirst, isLast 等。

final route = ModalRoute.of(context);
if (route.isCurrent) {
  // 当前路由是显示的路由
}

Navigator.onGenerateRoute() 方法

MaterialAppCupertinoApp 中,你可以使用 onGenerateRoute 回调来动态生成路由。

MaterialApp(
  onGenerateRoute: (settings) {
    if (settings.name == '/path') {
      return MaterialPageRoute(builder: (context) => NewScreen());
    }
    return null;
  },
);

Navigator.obscureBehavior 属性

这个属性定义了当新路由出现时,如何模糊或隐藏当前的路由。它接受一个 ObscureBehavior 值,可以是 ObscureBehavior.none, ObscureBehavior.fade, ObscureBehavior.blur 等。

Navigator(
  obscureBehavior: ObscureBehavior.fadeIn,
  pages: [MaterialPage(child: HomeScreen())],
);

路由传递参数

你可以通过 RouteSettings 来传递参数给下一个路由。

Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => NewScreen(),
    settings: RouteSettings(arguments: 'some arguments'),
  ),
).then((value) {
  // 处理返回值
});

// 在新页面中获取参数
final args = ModalRoute.of(context)!.settings.arguments;

路由结果

你可以在弹出路由时返回一个结果给前一个路由。

// 在新页面中
Navigator.pop(context, 'result');

// 在前一个页面中
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => NewScreen(),
  ),
).then((value) {
  if (value == 'result') {
    // 处理结果
  }
});

封装一个路由管理类

现在的路由跳转时api 编程式跳转,并且便于进行权限校验

        Padding(
                padding: const EdgeInsets.all(10.0),
                child: SizedBox(
                  height: 300,
                  child: ListView(
                    scrollDirection: Axis.horizontal,
                    children: cards.map((card) {
                      return CardAnimationHover(
                        card: card,
                        width: 200,
                        showAnimation: false,
                        onTap: () {
                          Navigator.push(
                            context,
                            MaterialPageRoute(
                              builder: (context) => DetailsPage(
                                title: card['header'].toString(),
                                coverUrl: card['image'].toString(),
                              ),
                            ),
                          );
                        },
                      );
                    }).toList(),
                  ),
                ),
              ),

跳转具体页面


/// 详情页面
class DetailsPage extends StatefulWidget {
// 确保 title 在构造函数中被初始化
   String title;
//   封面url
   String coverUrl;
   DetailsPage({super.key, required this.title, required this.coverUrl}  );
  
  State<DetailsPage> createState() => _DetailsPageState();
}

class _DetailsPageState extends State<DetailsPage> with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this);
  }

  
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  
  Widget build(BuildContext context) {
     return Scaffold(
        appBar: AppBar(
          leading: const BackButton(),
          actions: [
            IconButton(
              icon: const Icon(Icons.search),
              tooltip: '查找',
              onPressed: () {
                ScaffoldMessenger.of(context).showSnackBar(
                    const SnackBar(content: Text('This is a snackbar')));
              },
            ),
            IconButton(onPressed: (){

              Navigator.pop(context);
            }, icon: const Icon(Icons.more_vert))
          ],
          title: Text(widget.title),
        ),
        body: SafeArea(child: Column(
          children: [
            // 封面
            Container(
              height: 200,
              width: double.infinity,
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(16.0), // 设置圆角半径
              ),
              child: Image.network(widget.coverUrl,fit: BoxFit.fill),
            ),
            Expanded(child: Text("这是详细页面"))
          ],
        ))
    );
  }
}

除开构造器获取路由传递的数值还可以生命周期获取

import 'package:flutter/material.dart';

class DetailsPage extends StatefulWidget {
  
  _DetailsPageState createState() => _DetailsPageState();
}

class _DetailsPageState extends State<DetailsPage> {
  String? title;
  String? coverUrl;

  
  void initState() {
    super.initState();
    // 在初始化时获取传递的参数
    WidgetsBinding.instance.addPostFrameCallback((_) {
      final args = ModalRoute.of(context)?.settings.arguments as DetailPageArgs?;
      if (args != null) {
        setState(() {
          title = args.title;
          coverUrl = args.coverUrl;
        });
      }
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(title ?? 'Details')),
      body: Center(
        child: coverUrl != null
            ? Image.network(coverUrl!)
            : const Text('No cover image available'),
      ),
    );
  }
}

但是一般开发会封装一个专门的路由跳转的类 (鉴权,错误页面处理等)

基于navigator api封装的路由管理类


import 'package:flutter/material.dart';

class AppRouter {
  // 跳转到指定路由并传递参数
  static Future<T?> navigateTo<T>(
      BuildContext context, String routeName, {Object? arguments}) {
    return Navigator.pushNamed<T>(context, routeName, arguments: arguments);
  }

  // 跳转到指定路由并清除之前所有的路由(通常用于登录页面)
  static Future<T?> navigateAndReplaceAll<T>(
      BuildContext context, String routeName, {Object? arguments}) {
    return Navigator.pushNamedAndRemoveUntil<T>(
        context, routeName, (route) => false, arguments: arguments);
  }

  // 跳转到指定路由并替换当前路由
  static Future<T?> navigateAndReplace<T>(
      BuildContext context, String routeName, {Object? arguments}) {
    return Navigator.pushReplacementNamed<T, dynamic>(context, routeName,
        arguments: arguments);
  }

  // 返回上一页并传递数据
  static void goBack<T>(BuildContext context, [T? result]) {
    Navigator.pop<T>(context, result);
  }

  // 判断是否可以返回
  static bool canGoBack(BuildContext context) {
    return Navigator.canPop(context);
  }
}


路由类

根据不同路由返回不同的组件



// 路由生成器类
class RouteGenerator {
  static Route<dynamic> generateRoute(RouteSettings settings) {
    // 获取传递的参数
    final args = settings.arguments;

    switch (settings.name) {
      case '/':
      // 验证参数类型并传递
        if (args != null) {
          return _errorRoute();

        }
        // 参数不正确,跳转错误页面
        return MaterialPageRoute(builder: (_) => const Myhome());
      case '/details':
      // 期待传入一个带多个属性的对象(比如一个 Map 或自定义类)
        if (args is DetailPageArgs) {
          return MaterialPageRoute(
              builder: (_) => DetailsPage(
                title: args.title,
                coverUrl: args.coverUrl,
              ));
        }
        return _errorRoute();

      // case '/login':
      //   return MaterialPageRoute(builder: (_) => LoginScreen());

      default:
        return _errorRoute();
    }
  }

  // 错误路由页面
  static Route<dynamic> _errorRoute() {
    return MaterialPageRoute(
      builder: (_) => Scaffold(
        appBar: AppBar(title: const Text('出错啦')),
        body: const Center(child: Text('Page not found!',style: TextStyle(
          fontSize: 30
        ),)),
      ),
    );
  }
}


项目入口main中进行配置

void main() {
  runApp( MyApp());
}

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: '一乐动漫',
      initialRoute: '/',
      onGenerateRoute: RouteGenerator.generateRoute, // 使用路由管理器
    );
  }
}


具体使用

   onTap: () {
       AppRouter.navigateTo(context, '/details', arguments:
       DetailPageArgs(title: card['header'].toString(), coverUrl: card['image'].toString()));
                        },


页面返回时携带数据

// 跳转时传递参数
AppRouter.navigateTo(context, '/details', arguments: 'Some data').then((result) {
  // 接收返回的结果
  if (result != null) {
    print('返回结果: $result');
  }
});

//跳转的具体页面 点击返回的
Navigator.pop(context, 'This is the result');

抽屉

Flutter 提供了一个专门用于实现这种侧边菜单抽屉的控件,叫做 Drawer。它可以和 Scaffold 结合使用,提供左侧或右侧的抽屉菜单(类似于移动应用中常见的滑动菜单)。

典型的 Drawer 使用示例

以下是一个在 Flutter 中使用 Drawer 的基本示例。点击左上角的菜单图标(hamburger icon)可以打开从左侧滑出的抽屉菜单。

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Drawer Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Drawer Demo'),
      ),
      // 左侧抽屉菜单
      drawer: Drawer(
        child: ListView(
          padding: EdgeInsets.zero,
          children: <Widget>[
            DrawerHeader(
              decoration: BoxDecoration(
                color: Colors.blue,
              ),
              child: Text(
                'Menu',
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 24,
                ),
              ),
            ),
            ListTile(
              leading: Icon(Icons.home),
              title: Text('Home'),
              onTap: () {
                // 点击菜单后关闭抽屉
                Navigator.pop(context);
              },
            ),
            ListTile(
              leading: Icon(Icons.settings),
              title: Text('Settings'),
              onTap: () {
                Navigator.pop(context);
              },
            ),
            ListTile(
              leading: Icon(Icons.logout),
              title: Text('Logout'),
              onTap: () {
                Navigator.pop(context);
              },
            ),
          ],
        ),
      ),
      body: Center(
        child: Text('Swipe from left or click the menu icon to open drawer'),
      ),
    );
  }
}

关键点解析:

  • Scaffold: Flutter 提供的基本页面布局结构,Scaffold 可以帮助你快速搭建包含 AppBar、Drawer、BottomNavigationBar 等常见 UI 组件的页面。

  • drawer: Scaffolddrawer 属性允许你定义从左侧滑出的抽屉。可以在 Drawer 中放置任何 widget,常见的是使用 ListView 结合 ListTile 来创建菜单项。

  • DrawerHeader: 用于在抽屉顶部显示自定义内容(如头像、用户名等)。

  • 关闭抽屉: 使用 Navigator.pop(context) 关闭抽屉菜单。

从右侧弹出的抽屉

如果你想要从右侧滑出菜单,可以使用 ScaffoldendDrawer 属性。它的用法与 drawer 类似,但菜单会从右侧弹出。

Scaffold(
  appBar: AppBar(
    title: Text('End Drawer Demo'),
  ),
  // 右侧抽屉
  endDrawer: Drawer(
    child: ListView(
      padding: EdgeInsets.zero,
      children: <Widget>[
        DrawerHeader(
          decoration: BoxDecoration(
            color: Colors.blue,
          ),
          child: Text(
            'Right Menu',
            style: TextStyle(
              color: Colors.white,
              fontSize: 24,
            ),
          ),
        ),
        ListTile(
          leading: Icon(Icons.home),
          title: Text('Home'),
          onTap: () {
            Navigator.pop(context);
          },
        ),
        ListTile(
          leading: Icon(Icons.settings),
          title: Text('Settings'),
          onTap: () {
            Navigator.pop(context);
          },
        ),
      ],
    ),
  ),
  body: Center(
    child: Text('Swipe from right or click the menu icon to open end drawer'),
  ),
)

网络请求和json解析

在 Flutter 中,网络请求和 JSON 数据处理是非常常见的需求。通过使用 http 包进行网络请求,以及结合 Dart 自带的 dart:convert 库处理 JSON 数据,可以非常方便地实现与服务端的交互。下面我将详细介绍网络请求和 JSON 处理的具体步骤。

1. 导入依赖

在 Flutter 中使用 http 库来发起网络请求。在 pubspec.yaml 中添加依赖:

dependencies:
  http: ^0.13.3

然后在代码中导入 httpdart:convert

import 'package:http/http.dart' as http;
import 'dart:convert';

2. 发起网络请求

(1) GET 请求

GET 请求用于从服务器获取数据。以下是一个简单的 GET 请求示例:

Future<void> fetchData() async {
  final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1'));

  if (response.statusCode == 200) {
    // 请求成功,将响应体解析为 JSON
    var jsonResponse = jsonDecode(response.body);
    print('Title: ${jsonResponse['title']}');
  } else {
    // 请求失败,抛出异常
    throw Exception('Failed to load data');
  }
}

http.get 方法返回一个 Future<http.Response> 对象,异步等待请求完成。我们可以通过 jsonDecode 函数将返回的 JSON 数据转为 Dart 的 Map 或 List。

(2) POST 请求

POST 请求用于向服务器发送数据,例如提交表单。以下是一个简单的 POST 请求示例:

Future<void> postData() async {
  final response = await http.post(
    Uri.parse('https://jsonplaceholder.typicode.com/posts'),
    headers: {'Content-Type': 'application/json; charset=UTF-8'},
    body: jsonEncode(<String, String>{
      'title': 'Flutter',
      'body': 'Network request example',
      'userId': '1',
    }),
  );

  if (response.statusCode == 201) {
    // 请求成功,解析响应
    var jsonResponse = jsonDecode(response.body);
    print('Post created: ${jsonResponse['id']}');
  } else {
    // 请求失败,抛出异常
    throw Exception('Failed to create post');
  }
}

在 POST 请求中,body 是通过 jsonEncode 将 Dart 对象转为 JSON 字符串,然后发送给服务器。响应的处理与 GET 请求类似。

3. JSON 处理

(1) 解析 JSON 字符串

dart:convert 库中的 jsonDecode 函数可以将 JSON 字符串转换为 Dart 对象。

String jsonString = '{"name": "John", "age": 30}';
Map<String, dynamic> user = jsonDecode(jsonString);

print('Name: ${user['name']}');
print('Age: ${user['age']}');

在上面的例子中,jsonDecode 会将 JSON 字符串解析为 Map<String, dynamic> 对象。

(2) 将 Dart 对象转换为 JSON 字符串

jsonEncode 函数可以将 Dart 对象转换为 JSON 字符串,通常用于发送 POST 请求时。

Map<String, dynamic> user = {
  'name': 'John',
  'age': 30,
};

String jsonString = jsonEncode(user);
print(jsonString); // 输出 {"name":"John","age":30}

4. 结合模型类处理 JSON

为了简化代码,并确保 JSON 解析和序列化的正确性,建议将 JSON 转换为模型类对象。以下是一个简单的例子。

(1) 创建模型类
class Post {
  final int id;
  final String title;
  final String body;

  Post({required this.id, required this.title, required this.body});

  // 工厂方法:从 JSON 构造 Post 对象
  factory Post.fromJson(Map<String, dynamic> json) {
    return Post(
      id: json['id'],
      title: json['title'],
      body: json['body'],
    );
  }

  // 将 Post 对象转换为 JSON
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'title': title,
      'body': body,
    };
  }
}
(2) 使用模型类解析 JSON
Future<void> fetchPost() async {
  final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1'));

  if (response.statusCode == 200) {
    // 解析 JSON 并创建 Post 对象
    var jsonResponse = jsonDecode(response.body);
    Post post = Post.fromJson(jsonResponse);
    print('Post title: ${post.title}');
  } else {
    throw Exception('Failed to load post');
  }
}
(3) 将对象转换为 JSON
Post post = Post(id: 1, title: 'Flutter', body: 'Network request example');
String jsonPost = jsonEncode(post.toJson());
print(jsonPost);

通过将 JSON 数据映射到模型类,可以使代码更加清晰、易于维护。

5. 异常处理

在进行网络请求时,可能会出现各种错误,例如网络不可用、请求超时等。可以通过 try-catch 进行异常捕获。

Future<void> fetchData() async {
  try {
    final response = await http.get(Uri.parse('https://example.com/data'));
    if (response.statusCode == 200) {
      var jsonResponse = jsonDecode(response.body);
      print(jsonResponse);
    } else {
      print('Server error: ${response.statusCode}');
    }
  } catch (error) {
    print('Error: $error');
  }
}

没错!在 Flutter 中,尽管 http 模块是内置的网络请求解决方案,但很多开发者更倾向于使用功能更丰富的第三方库,比如 DioDio 是一个强大且易用的网络请求库,提供了丰富的功能,如拦截器、全局配置、文件上传和下载、取消请求等。

http 插件dio

为什么使用 Dio?

  1. 更强的功能:Dio 支持网络请求的拦截器、全局配置、文件上传/下载、表单数据提交等。
  2. 错误处理更灵活:Dio 提供了更全面的错误处理机制,方便管理和追踪各种类型的错误。
  3. 更好的性能:Dio 在某些场景下比 http 更加优化,并且支持配置请求超时时间和请求重试等功能。
  4. 容易集成拦截器:可以轻松添加拦截器以处理请求、响应、错误等,便于实现例如日志记录、权限校验等功能。

1. 在 pubspec.yaml 中添加 Dio 依赖

dependencies:
  dio: ^5.3.1

然后在代码中导入 Dio:

import 'package:dio/dio.dart';

2. 基本使用

(1) 发起 GET 请求
Future<void> fetchData() async {
  Dio dio = Dio();
  try {
    Response response = await dio.get('https://jsonplaceholder.typicode.com/posts/1');
    print('Response data: ${response.data}');
  } catch (e) {
    print('Error occurred: $e');
  }
}

dio.get() 返回一个 Response 对象,其中 response.data 可以直接访问返回的数据,Dio 会自动处理 JSON 解码。


void main() {
  test('测试dio网络请求', () async {
    Dio dio = Dio();
    try {
      Response response = await dio.get('http://localhost:8080/api/users');
      /**
       * 响应数据: {t: {name: 测试对象, age: 666}, msg: success, code: 0}
       */
      print('响应数据: ${response.data}');

      // 直接使用 response.data
      var data = response.data;
      print(data);

      // 如果可以直接读取dio解析的json对象,但是这样的话 不易阅读
      if (data['code'] == 0) {
        var t = data['t'];
        print('名称: ${t['name']}, 年龄: ${t['age']}');
      } else {
        print('请求失败: ${data['msg']}');
      }
    } catch (e) {
      print('错误异常: $e');
    }
  });
  test('测试dio网络请求json和dart对象互转', () async {
    Dio dio = Dio();
    try {
      Response response = await dio.get('http://localhost:8080/api/users');
      /**
       * 响应数据: {t: {name: 测试对象, age: 666}, msg: success, code: 0}
       */
      print('响应数据: ${response.data}');

      // 使用模型类解析数据
      var apiResponse = ApiResponse.fromJson(response.data);
      print(apiResponse);

      // 如果需要进一步处理数据
      if (apiResponse.code == 0) {
        print('名称: ${apiResponse.t.name}, 年龄: ${apiResponse.t.age}');
      } else {
        print('请求失败: ${apiResponse.msg}');
      }
    } catch (e) {
      print('错误异常: $e');
    }
  });
}
class ApiResponse {
  final int code;
  final String msg;
  final User t;

  ApiResponse({required this.code, required this.msg, required this.t});

  factory ApiResponse.fromJson(Map<String, dynamic> json) {
    return ApiResponse(
      code: json['code'],
      msg: json['msg'],
      t: User.fromJson(json['t']),
    );
  }
}

class User {
  final String name;
  final int age;

  User({required this.name, required this.age});

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      name: json['name'],
      age: json['age'],
    );
  }
}


(2) 发起 POST 请求
Future<void> postData() async {
  Dio dio = Dio();
  try {
    Response response = await dio.post(
      'https://jsonplaceholder.typicode.com/posts',
      data: {
        'title': 'Flutter Dio',
        'body': 'This is a Dio post request example',
        'userId': 1,
      },
    );
    print('Response data: ${response.data}');
  } catch (e) {
    print('Error occurred: $e');
  }
}

在 POST 请求中,数据可以通过 data 参数发送,并且支持自动将 Dart 的 Map 对象转为 JSON。

3. 使用拦截器

Dio 提供了拦截器来在请求发出前或响应返回时执行自定义逻辑,这对于处理 token、全局错误处理等非常有用。

添加请求拦截器和响应拦截器
Dio dio = Dio();

dio.interceptors.add(InterceptorsWrapper(
  onRequest: (options, handler) {
    print('Request: ${options.method} ${options.path}');
    return handler.next(options); // 继续执行请求
  },
  onResponse: (response, handler) {
    print('Response: ${response.statusCode}');
    return handler.next(response); // 继续执行响应
  },
  onError: (DioError e, handler) {
    print('Error: ${e.message}');
    return handler.next(e); // 继续处理错误
  },
));

通过拦截器,您可以在网络请求的各个阶段执行自定义逻辑,例如在每个请求前自动添加身份验证 token,或者在响应中统一处理错误。

4. 全局配置

Dio 可以为所有请求设置全局的配置,例如超时时间、请求头等:

Dio dio = Dio(BaseOptions(
  baseUrl: 'https://jsonplaceholder.typicode.com',
  connectTimeout: Duration(seconds: 5),
  receiveTimeout: Duration(seconds: 5),
  headers: {
    'Content-Type': 'application/json; charset=UTF-8',
  },
));

Future<void> fetchData() async {
  try {
    Response response = await dio.get('/posts/1');
    print('Response data: ${response.data}');
  } catch (e) {
    print('Error occurred: $e');
  }
}

在这里,我们通过 BaseOptions 为 Dio 实例设置了基础配置,后续的每个请求都将继承这些配置。

5. 文件上传和下载

(1) 文件上传

Dio 支持表单数据提交,非常适合用于上传文件:

Future<void> uploadFile(String filePath) async {
  Dio dio = Dio();

  FormData formData = FormData.fromMap({
    'file': await MultipartFile.fromFile(filePath, filename: 'upload.png'),
  });

  try {
    Response response = await dio.post(
      'https://example.com/upload',
      data: formData,
    );
    print('File uploaded: ${response.data}');
  } catch (e) {
    print('Upload error: $e');
  }
}

FormData 可以处理多种类型的数据,包括文件上传。MultipartFile.fromFile() 方法会将本地文件转换为上传的表单文件。

(2) 文件下载
Future<void> downloadFile() async {
  Dio dio = Dio();
  try {
    await dio.download(
      'https://example.com/file.zip',
      '/path/to/save/file.zip',
      onReceiveProgress: (received, total) {
        if (total != -1) {
          print('Downloading: ${(received / total * 100).toStringAsFixed(0)}%');
        }
      },
    );
  } catch (e) {
    print('Download error: $e');
  }
}

Dio 提供了 download 方法用于文件下载,并且可以通过 onReceiveProgress 回调函数实时获取下载进度。

6. 取消请求

Dio 提供了取消请求的功能,非常适合处理用户发起多个重复请求或长时间等待的操作。可以通过 CancelToken 来控制请求的取消。

CancelToken cancelToken = CancelToken();

Future<void> fetchData() async {
  Dio dio = Dio();

  try {
    Response response = await dio.get(
      'https://jsonplaceholder.typicode.com/posts/1',
      cancelToken: cancelToken,
    );
    print('Response data: ${response.data}');
  } catch (e) {
    if (CancelToken.isCancel(e)) {
      print('Request cancelled');
    } else {
      print('Error: $e');
    }
  }
}

// 在需要的时候取消请求
cancelToken.cancel('Request cancelled by user');

封装

和axios一样可以封装实列使用

import 'package:dio/dio.dart';

class DioClient {
  static DioClient? _instance;
  late Dio _dio;

  // 私有构造函数
  DioClient._internal() {
    _dio = Dio(BaseOptions(
      baseUrl: "https://your-api.com", // 设置基础URL
      connectTimeout: const Duration(seconds: 10),
      receiveTimeout: const Duration(seconds: 10),
      headers: {
        'Content-Type': 'application/json',
      },
    ));

    // 添加拦截器
    _dio.interceptors.add(InterceptorsWrapper(
      onRequest: (options, handler) {
        // 在请求发送前做一些处理,如添加公共token
        options.headers['Authorization'] = 'Bearer your_token';
        print('REQUEST[${options.method}] => PATH: ${options.path}');
        handler.next(options); // 继续下一个拦截器
      },
      onResponse: (response, handler) {
        // 处理响应
        print('RESPONSE[${response.statusCode}] => PATH: ${response.requestOptions.path}');
        handler.next(response);
      },
      onError: (DioError e, handler) {
        // 处理错误
        print('ERROR[${e.response?.statusCode}] => PATH: ${e.requestOptions.path}');
        handler.next(e); // 继续下一个拦截器
      },
    ));

    // 可选:日志拦截器,用于在开发时打印请求和响应
    _dio.interceptors.add(LogInterceptor(
      request: true,
      requestHeader: true,
      requestBody: true,
      responseHeader: true,
      responseBody: true,
      error: true,
    ));
  }

  // 单例模式
  static DioClient getInstance() {
    _instance ??= DioClient._internal();
    return _instance!;
  }

  // GET 请求
  Future<Response> get(String path, {Map<String, dynamic>? queryParams}) async {
    try {
      Response response = await _dio.get(path, queryParameters: queryParams);
      return response;
    } catch (e) {
      return Future.error(_handleError(e));
    }
  }

  // POST 请求
  Future<Response> post(String path, {Map<String, dynamic>? data}) async {
    try {
      Response response = await _dio.post(path, data: data);
      return response;
    } catch (e) {
      return Future.error(_handleError(e));
    }
  }

  // PUT 请求
  Future<Response> put(String path, {Map<String, dynamic>? data}) async {
    try {
      Response response = await _dio.put(path, data: data);
      return response;
    } catch (e) {
      return Future.error(_handleError(e));
    }
  }

  // DELETE 请求
  Future<Response> delete(String path, {Map<String, dynamic>? data}) async {
    try {
      Response response = await _dio.delete(path, data: data);
      return response;
    } catch (e) {
      return Future.error(_handleError(e));
    }
  }

  // Token 自动刷新逻辑(可根据具体情况修改)
  Future<void> _refreshToken() async {
    // 刷新 token 的逻辑
    // 如果 token 刷新成功,更新请求头中的 token
    _dio.options.headers['Authorization'] = 'Bearer new_token';
  }

  // 处理错误
  String _handleError(dynamic error) {
    if (error is DioError) {
      switch (error.type) {
        case DioErrorType.connectTimeout:
          return "Connection Timeout!";
        case DioErrorType.sendTimeout:
          return "Send Timeout!";
        case DioErrorType.receiveTimeout:
          return "Receive Timeout!";
        case DioErrorType.response:
          return "Received invalid status code: ${error.response?.statusCode}";
        case DioErrorType.cancel:
          return "Request to API server was cancelled";
        case DioErrorType.other:
          return "Connection to API server failed due to internet connection";
        default:
          return "Unexpected error occured";
      }
    } else {
      return "Unexpected error occured";
    }
  }
}


实列化
DioClient dioClient = DioClient.getInstance();
发送请求
var response = await dioClient.get(‘/api/v1/resource’);

总结

相比 http 库,Dio 提供了更丰富的功能,尤其是在处理拦截器、全局配置、文件上传和下载、取消请求等方面。通过使用 Dio,你可以更方便地管理复杂的网络请求逻辑,并保持代码简洁和易维护。

你可以根据项目需求选择适合的网络请求库,但如果你的项目复杂度较高且需要更多的控制,Dio 会是一个非常合适的选择。

样式

flutter的每个控件的样式类都不一样,很容易记混,所以进行总结

在 Flutter 中,控件样式的定义是非常灵活的,有些控件有类似的样式属性,但不同类型的控件又可能有特定的样式定义。为了方便你记忆,我将根据控件的类型对常用的样式属性进行归纳总结。

1. 通用样式属性

以下属性是 Flutter 中大部分控件共享的样式属性,尤其是涉及到 ContainerTextButton 等常见控件:
通用样式属性在 Flutter 中适用于许多控件,尤其是像 ContainerTextButton 等控件,几乎所有的 UI 组件都可以设置一些通用的样式属性。为了帮助你更详细地理解通用样式属性,我将对每个属性进行详细解释,并提供相应的代码示例。

color

color 是设置控件的背景颜色的属性。它常见于 ContainerTextButtonScaffold 等控件。

用法:
Container(
  color: Colors.blue,  // 设置背景颜色为蓝色
  child: Text('This is a Container'),
)
注意:
  • 有些控件需要通过 decoration 来设置背景颜色,例如 Container 中,当 decoration 设置时,不能直接使用 color 属性。
  • Text 控件的颜色需要通过 TextStylecolor 属性来设置。
padding

padding 是设置控件内部内容的边距(内边距)。通过 EdgeInsets 来定义四个方向的距离。

用法:
Container(
  padding: EdgeInsets.all(16.0),  // 设置四周内边距为16像素
  color: Colors.grey,
  child: Text('This is a padded Container'),
)
常见的 EdgeInsets 构造函数:
  • EdgeInsets.all(double value):四个方向的边距相同。
  • EdgeInsets.symmetric({double vertical, double horizontal}):设置水平或垂直方向的对称内边距。
  • EdgeInsets.only({double left, double top, double right, double bottom}):分别设置某个方向的边距。
margin

margin 是设置控件外部与其他控件之间的间距(外边距)。与 padding 类似,也使用 EdgeInsets 进行定义。

用法:
Container(
  margin: EdgeInsets.all(20.0),  // 设置四周外边距为20像素
  color: Colors.green,
  child: Text('This Container has a margin'),
)
注意:
  • margin 是容器之外的空间,而 padding 是容器内部的空间。
alignment

alignment 用于控制容器内子控件的对齐方式。常用于 ContainerAlign 等控件。

用法:
Container(
  alignment: Alignment.center,  // 将子控件居中对齐
  color: Colors.yellow,
  child: Text('Centered text'),
)
常见的 Alignment 常量:
  • Alignment.center:子控件居中。
  • Alignment.topLeft:子控件左上角对齐。
  • Alignment.bottomRight:子控件右下角对齐。
细节:

Alignment 是一个 2D 平面的坐标系,中心为 (0,0),向左为负数,向右为正数,向上为负数,向下为正数。你也可以通过 Alignment(x, y) 自定义位置。

. decoration

decoration 属性通过 BoxDecoration 来设置更加丰富的样式,适用于 Container 等控件。它可以设置背景颜色、背景图像、圆角、阴影、边框等。

用法:
Container(
  decoration: BoxDecoration(
    color: Colors.red,  // 设置背景颜色
    borderRadius: BorderRadius.circular(10),  // 设置圆角
    boxShadow: [
      BoxShadow(
        color: Colors.black.withOpacity(0.5),  // 阴影颜色
        offset: Offset(2, 4),  // 阴影偏移
        blurRadius: 5,  // 阴影模糊半径
      ),
    ],
  ),
  child: Text('Decorated Container'),
)
其他属性:
  • border:设置边框样式。
  • gradient:设置渐变背景。
  • image:设置背景图片,使用 DecorationImage
注意:

如果 Container 同时设置了 colordecorationcolor 会被忽略。

widthheight

widthheight 用于设置控件的宽度和高度,通常用于容器控件如 ContainerSizedBox

用法:
Container(
  width: 200.0,  // 宽度 200 像素
  height: 100.0, // 高度 100 像素
  color: Colors.blue,
  child: Center(child: Text('Fixed size container')),
)

如果标识沾满 ,无限大使用double.infinity, 源码是1.0/0.0

注意:
  • 如果 Container 的子控件有固定大小,那么 widthheight 可能会受到子控件的影响。
constraints

constraints 属性通过 BoxConstraints 设置控件的尺寸约束,如最小宽度、最大高度等。

用法:
Container(
  constraints: BoxConstraints(
    minWidth: 100,  // 最小宽度
    maxWidth: 200,  // 最大宽度
    minHeight: 50,  // 最小高度
    maxHeight: 150, // 最大高度
  ),
  color: Colors.orange,
  child: Text('Constrained Container'),
)
常用的 BoxConstraints
  • BoxConstraints.tight(Size size):强制控件固定尺寸。
  • BoxConstraints.loose(Size size):允许控件小于指定尺寸。
  • BoxConstraints.expand():扩展控件以占据所有可用空间。
transform

transform 用于在控件绘制时进行几何变换,包括旋转、缩放、平移等。它接收一个 Matrix4 对象来定义变换方式。

用法:
Container(
  color: Colors.purple,
  transform: Matrix4.rotationZ(0.1),  // 绕 Z 轴旋转
  child: Text('Rotated Container'),
)
常见变换类型:
  • Matrix4.translationValues(double x, double y, double z):平移。
  • Matrix4.rotationZ(double radians):绕 Z 轴旋转。
  • Matrix4.diagonal3Values(double x, double y, double z):缩放。
注意:

transform 影响控件的渲染位置,但不会改变它实际的布局空间。

child

child 是几乎所有容器类控件中都有的属性,表示容器内部的子控件。大多数情况下,容器类控件只能包含一个子控件,如果需要包含多个子控件,可以使用布局控件如 ColumnRow 等。

用法:
Container(
  color: Colors.teal,
  child: Text('This is a child widget'),
)
decorationforegroundDecoration
  • decoration:用于设置控件背景的装饰样式,如背景颜色、边框、阴影等。
  • foregroundDecoration:和 decoration 类似,但应用在内容的前景(覆盖在 child 之上)。
用法:
Container(
  decoration: BoxDecoration(
    color: Colors.yellow,
    border: Border.all(color: Colors.red, width: 2),
  ),
  foregroundDecoration: BoxDecoration(
    color: Colors.black.withOpacity(0.5), // 在前景添加半透明黑色覆盖层
  ),
  child: Text('Container with foreground decoration'),
)

2. 文本样式 (TextStyle)

文本控件 (Text) 的样式属性,定义了字体、大小、颜色等。

  • fontSize:字体大小。

    fontSize: 16.0
    
  • fontWeight:字体粗细,可使用 FontWeight.boldFontWeight.w400

    fontWeight: FontWeight.bold
    
  • color:文本颜色。

    color: Colors.black
    
  • fontFamily:指定字体族。

    fontFamily: 'Roboto'
    
  • letterSpacing:字母间距。

    letterSpacing: 2.0
    
  • decoration:文本装饰,如下划线、删除线等。

    decoration: TextDecoration.underline
    
  • height:行高,通常是字体大小的倍数。

    height: 1.5
    

3. 按钮样式

Flutter 的按钮控件(如 ElevatedButtonTextButtonOutlinedButton)有统一的样式系统,通过 ButtonStyle 来定义。常用的按钮样式属性有:

  • backgroundColor:按钮背景颜色。

    backgroundColor: MaterialStateProperty.all(Colors.blue)
    
  • foregroundColor:按钮上的文本或图标的颜色。

    foregroundColor: MaterialStateProperty.all(Colors.white)
    
  • shape:按钮的形状,如圆角按钮。

    shape: MaterialStateProperty.all(
      RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(18.0),
      ),
    )
    
  • side:边框样式,适用于 OutlinedButton

    side: MaterialStateProperty.all(
      BorderSide(color: Colors.blue, width: 2.0),
    )
    
  • padding:按钮内的填充空间。

    padding: MaterialStateProperty.all(EdgeInsets.all(16.0))
    
  • elevation:按钮的阴影效果,适用于 ElevatedButton

    elevation: MaterialStateProperty.all(5.0)
    

4. 容器样式 (Container)

Container 是一个灵活的控件,几乎可以包含所有样式属性。除了上面提到的通用样式,Container 还支持:

  • widthheight:容器的宽度和高度。

    width: 100,
    height: 50,
    
  • constraints:设置容器的尺寸约束。

    constraints: BoxConstraints(
      minWidth: 100,
      minHeight: 50,
      maxWidth: 200,
      maxHeight: 100,
    )
    
  • transform:用于在绘制时应用旋转、缩放、平移等变换。

    transform: Matrix4.rotationZ(0.1),
    
  • decoration:用于设置容器的背景颜色、渐变、阴影、边框等样式。

    decoration: BoxDecoration(
      color: Colors.blue,
      borderRadius: BorderRadius.circular(10),
      boxShadow: [
        BoxShadow(
          color: Colors.grey.withOpacity(0.5),
          spreadRadius: 5,
          blurRadius: 7,
          offset: Offset(0, 3),
        ),
      ],
    )
    

5. 图片样式 (Image)

图片控件(Image)也有一些特定的样式属性:

  • fit:图片的适应模式,用于控制图片如何填充容器,比如 BoxFit.cover

    fit: BoxFit.cover
    
  • widthheight:图片的宽度和高度。

    width: 100,
    height: 100,
    
  • alignment:图片的对齐方式。

    alignment: Alignment.center
    
  • colorcolorBlendMode:应用颜色和混合模式到图片上。

    color: Colors.red,
    colorBlendMode: BlendMode.colorBurn,
    

6. 列表样式

ListViewGridView 中,每个项的样式往往与容器类似,可以通过 paddingmargindecoration 来控制样式。此外,列表特有的样式包括:

  • scrollDirection:滚动方向,水平或垂直。

    scrollDirection: Axis.horizontal
    
  • shrinkWrap:是否根据内容收缩,适用于在嵌套列表中防止无限滚动。

    shrinkWrap: true,
    
  • physics:控制滚动行为,如 BouncingScrollPhysics(滚动回弹效果)或 NeverScrollableScrollPhysics(禁止滚动)。

    physics: BouncingScrollPhysics()
    

7. 布局样式

布局控件(如 RowColumnStack 等)有一些特定的样式属性:

  • mainAxisAlignment:主轴对齐方式,如 MainAxisAlignment.center

    mainAxisAlignment: MainAxisAlignment.center,
    
  • crossAxisAlignment:交叉轴对齐方式,如 CrossAxisAlignment.start

    crossAxisAlignment: CrossAxisAlignment.start,
    
  • spacing:在 Wrap 布局中,用于控制子控件之间的间距。

    spacing: 10.0,
    
  • overflow:在 Stack 中控制子控件溢出时的处理方式,如 Overflow.visible

    overflow: Overflow.visible,
    

总结

  • 通用样式:颜色、对齐方式、边距、内边距等可广泛应用于大多数控件。
  • 文本样式:通过 TextStyle 定制字体、大小、颜色、间距等。
  • 按钮样式:按钮具有 ButtonStyle 来控制背景、边框、阴影等。
  • 容器样式Container 拥有灵活的尺寸、装饰、变换等属性。
  • 图片样式:控制图片的适应模式、尺寸、颜色滤镜等。
  • 列表和布局样式:控制滚动方向、对齐方式、间距等布局特性。

由于控件特别多 篇幅原因根本写不完,下面给一个大概目录介绍,实际用法可以使用gpt 进行了解(gpt 都比文档解释明白)

超详细的 Flutter 控件大全

目录

  1. 基础控件
    • Text
    • RichText
    • Image
    • Icon
    • Placeholder
  2. 布局控件
    • Container
    • Padding
    • Center
    • Align
    • SizedBox
    • Expanded
    • Flexible
    • Row
    • Column
    • Stack
    • Wrap
    • Flow
    • Table
    • GridView
    • ListView
  3. 输入控件
    • TextField
    • Checkbox
    • Radio
    • Switch
    • Slider
    • Form
    • DropdownButton
  4. 按钮控件
    • ElevatedButton
    • TextButton
    • OutlinedButton
    • IconButton
    • FloatingActionButton
    • PopupMenuButton
  5. 导航控件
    • AppBar
    • BottomNavigationBar
    • Drawer
    • TabBar
    • TabBarView
    • Navigator
    • PageView
  6. 动画与过渡控件
    • AnimatedContainer
    • AnimatedOpacity
    • Hero
    • Transform
    • AnimationController
    • FadeTransition
    • ScaleTransition
    • SizeTransition
    • PositionedTransition
    • SlideTransition
  7. 样式与主题控件
    • Theme
    • MediaQuery
    • DefaultTextStyle
    • IconTheme
    • Builder
  8. 异步与状态管理控件
    • StatefulWidget
    • StatelessWidget
    • FutureBuilder
    • StreamBuilder
  9. 滚动控件
    • SingleChildScrollView
    • Scrollbar
    • CustomScrollView
    • NotificationListener
  10. 绘制与效果控件
    • Opacity
    • ClipRect
    • ClipRRect
    • ClipOval
    • ClipPath
    • CustomPaint
  11. 交互模型
    • GestureDetector
    • Dismissible
    • Draggable
    • LongPressDraggable
    • DragTarget
  12. Material 组件
    • Scaffold
    • MaterialApp
    • Material
    • Card
    • Chip
    • SnackBar
    • Dialog
    • AlertDialog
    • SimpleDialog
    • BottomSheet
    • ExpansionPanel
  13. Cupertino(iOS 风格)控件
    • CupertinoApp
    • CupertinoButton
    • CupertinoNavigationBar
    • CupertinoTabScaffold
    • CupertinoAlertDialog

基础控件

Text

描述:用于显示一行简单格式的文本。

用法

Text(
  'Hello, Flutter!',
  style: TextStyle(
    fontSize: 24,
    color: Colors.blue,
    fontWeight: FontWeight.bold,
    fontStyle: FontStyle.italic,
    letterSpacing: 2.0,
    wordSpacing: 5.0,
    decoration: TextDecoration.underline,
    decorationColor: Colors.red,
    decorationStyle: TextDecorationStyle.dashed,
  ),
  textAlign: TextAlign.center,
  overflow: TextOverflow.ellipsis,
  maxLines: 2,
)

主要属性

  • data:要显示的文本。
  • style:文本样式,使用 TextStyle
  • textAlign:文本对齐方式,如 TextAlign.center
  • overflow:文本溢出处理方式,如 TextOverflow.ellipsis(省略号)。
  • maxLines:最大显示行数。
  • softWrap:是否自动换行。

RichText

描述:显示多种样式的富文本,可以对文本的不同部分应用不同的样式。

用法

RichText(
  text: TextSpan(
    text: 'Hello ',
    style: TextStyle(fontSize: 18, color: Colors.black),
    children: <TextSpan>[
      TextSpan(
        text: 'Flutter',
        style: TextStyle(fontWeight: FontWeight.bold, color: Colors.blue),
      ),
      TextSpan(text: '!'),
    ],
  ),
)

主要属性

  • text:要显示的文本,使用 TextSpan 组合。
  • textAlign:文本对齐方式。
  • textDirection:文本方向。
  • softWrap:是否自动换行。
  • overflow:文本溢出处理方式。

Image

描述:用于显示图片,可以从多种来源加载图片。

用法

Image.network(
  'https://example.com/image.png',
  width: 100,
  height: 100,
  fit: BoxFit.cover,
  color: Colors.red,
  colorBlendMode: BlendMode.colorBurn,
)

主要属性

  • image:要显示的图片,使用 ImageProvider
  • widthheight:图片的宽高。
  • fit:图片的适应方式,如 BoxFit.cover
  • alignment:图片的对齐方式。
  • repeat:图片的重复方式。
  • colorcolorBlendMode:颜色和混合模式。

示例

Image.asset(
  'assets/images/flutter_logo.png',
  width: 200,
  height: 200,
)

Icon

描述:用于显示图标,图标来自于字体库,如 Material Icons。

用法

Icon(
  Icons.favorite,
  color: Colors.pink,
  size: 24.0,
  semanticLabel: 'Favorite',
)

主要属性

  • icon:要显示的图标,使用 IconData
  • size:图标大小。
  • color:图标颜色。
  • semanticLabel:语义标签。

Placeholder

描述:一个占位控件,用于在布局时占据空间,表示将来会添加其他控件。

用法

Placeholder(
  color: Colors.grey,
  strokeWidth: 2.0,
  fallbackWidth: 100.0,
  fallbackHeight: 100.0,
)

主要属性

  • color:占位符的颜色。
  • strokeWidth:线条宽度。
  • fallbackWidthfallbackHeight:当约束无效时的默认宽高。

布局控件

Container

描述:一个方便的容器控件,结合了绘制、定位和尺寸调整的常见操作。

用法

Container(
  width: 200,
  height: 200,
  padding: EdgeInsets.all(16.0),
  margin: EdgeInsets.symmetric(horizontal: 20.0),
  alignment: Alignment.center,
  decoration: BoxDecoration(
    color: Colors.blue,
    borderRadius: BorderRadius.circular(10.0),
    border: Border.all(color: Colors.black, width: 2.0),
    boxShadow: [
      BoxShadow(
        color: Colors.grey.withOpacity(0.5),
        offset: Offset(0, 3),
        blurRadius: 7,
        spreadRadius: 5,
      ),
    ],
  ),
  child: Text('Container 示例', style: TextStyle(color: Colors.white)),
)

主要属性

  • alignment:子控件的对齐方式。
  • padding:内边距。
  • color:背景颜色。
  • decoration:背景装饰,如颜色、渐变、边框等。
  • widthheight:容器的宽高。
  • constraints:添加额外的约束条件。
  • margin:外边距。
  • transform:在绘制时应用的变换。

Padding

描述:一个小部件,用于在其子部件周围插入给定的填充。

用法

Padding(
  padding: EdgeInsets.fromLTRB(10, 20, 10, 20),
  child: Text('Padding 示例'),
)

主要属性

  • padding:填充的值,使用 EdgeInsets
  • child:子控件。

Center

描述:将其子控件居中显示。

用法

Center(
  child: Text('居中文本'),
)

主要属性

  • widthFactorheightFactor:可选参数,用于控制 Center 的尺寸。
  • child:子控件。

Align

描述:根据 Alignment 对齐其子控件,并可根据子控件的大小调整自身大小。

用法

Align(
  alignment: Alignment.bottomRight,
  child: Text('右下角对齐'),
)

主要属性

  • alignment:对齐方式,使用 Alignment
  • widthFactorheightFactor:可选参数,用于控制 Align 的尺寸。
  • child:子控件。

SizedBox

描述:具有特定尺寸的盒子,可用于给子控件指定固定的宽高。

用法

SizedBox(
  width: 100,
  height: 100,
  child: ElevatedButton(
    onPressed: () {},
    child: Text('固定尺寸按钮'),
  ),
)

主要属性

  • widthheight:宽度和高度。
  • child:子控件。

Expanded

描述:用于扩展 RowColumnFlex 中的子控件,以填充可用空间。

用法

Row(
  children: <Widget>[
    Expanded(
      flex: 1,
      child: Container(color: Colors.red),
    ),
    Expanded(
      flex: 2,
      child: Container(color: Colors.green),
    ),
  ],
)

主要属性

  • flex:占用空间的比例。
  • child:子控件。

Flexible

描述:一个控件,可以控制 RowColumnFlex 子控件的弹性行为。

用法

Row(
  children: <Widget>[
    Flexible(
      flex: 1,
      fit: FlexFit.tight,
      child: Container(color: Colors.blue),
    ),
    Flexible(
      flex: 2,
      fit: FlexFit.loose,
      child: Container(color: Colors.orange),
    ),
  ],
)

主要属性

  • flex:占用空间的比例。
  • fit:弹性系数,FlexFit.tightFlexFit.loose
  • child:子控件。

Row

描述:沿水平方向排列子控件的布局。

用法

Row(
  mainAxisAlignment: MainAxisAlignment.spaceAround,
  crossAxisAlignment: CrossAxisAlignment.center,
  children: <Widget>[
    Icon(Icons.home),
    Icon(Icons.star),
    Icon(Icons.person),
  ],
)

主要属性

  • children:子控件列表。
  • mainAxisAlignment:主轴(水平)方向的对齐方式。
  • crossAxisAlignment:交叉轴(垂直)方向的对齐方式。
  • mainAxisSize:主轴尺寸,MainAxisSize.maxMainAxisSize.min

Column

描述:沿垂直方向排列子控件的布局。

用法

Column(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  crossAxisAlignment: CrossAxisAlignment.start,
  children: <Widget>[
    Text('第一行'),
    Text('第二行'),
    Text('第三行'),
  ],
)

主要属性

  • Row 类似,只是方向为垂直方向。

Stack

描述:可以将子控件堆叠在一起,后添加的控件会覆盖在之前的控件之上。

用法

Stack(
  alignment: Alignment.center,
  children: <Widget>[
    Container(width: 200, height: 200, color: Colors.red),
    Container(width: 150, height: 150, color: Colors.green),
    Positioned(
      bottom: 10,
      right: 10,
      child: Text('定位文本'),
    ),
  ],
)

主要属性

  • alignment:未定位子控件的对齐方式。
  • fit:未定位子控件如何适应 Stack 的大小。
  • overflow:溢出处理方式(已废弃,使用 clipBehavior)。

Wrap

描述:当空间不足时,自动换行排列子控件,类似于网页的自动换行布局。

用法

Wrap(
  spacing: 8.0,
  runSpacing: 4.0,
  alignment: WrapAlignment.center,
  children: <Widget>[
    Chip(label: Text('标签1')),
    Chip(label: Text('标签2')),
    Chip(label: Text('标签3')),
    Chip(label: Text('标签4')),
    Chip(label: Text('标签5')),
  ],
)

主要属性

  • direction:布局方向,水平或垂直。
  • alignment:主轴对齐方式。
  • spacing:主轴方向子控件之间的间距。
  • runSpacing:交叉轴方向子控件之间的间距。
  • children:子控件列表。

Flow

描述:一个高效的控件,可以自定义子控件的位置,适用于需要高度自定义布局的场景。

用法

Flow(
  delegate: MyFlowDelegate(),
  children: <Widget>[
    Container(width: 80, height: 80, color: Colors.red),
    Container(width: 80, height: 80, color: Colors.green),
    Container(width: 80, height: 80, color: Colors.blue),
  ],
)

主要属性

  • delegate:控制子控件布局的委托类,需要继承 FlowDelegate
  • children:子控件列表。

Table

描述:创建一个表格布局,按行和列排列子控件。

用法

Table(
  border: TableBorder.all(color: Colors.black),
  children: [
    TableRow(children: [
      Text('单元格1'),
      Text('单元格2'),
      Text('单元格3'),
    ]),
    TableRow(children: [
      Text('单元格4'),
      Text('单元格5'),
      Text('单元格6'),
    ]),
  ],
)

主要属性

  • childrenTableRow 的列表,每个 TableRow 包含一行的子控件。
  • border:表格边框。
  • defaultColumnWidth:默认列宽。

GridView

描述:可滚动的网格列表,用于以网格形式显示数据。

用法

GridView.count(
  crossAxisCount: 2,
  crossAxisSpacing: 10.0,
  mainAxisSpacing: 10.0,
  children: <Widget>[
    Container(color: Colors.red),
    Container(color: Colors.green),
    Container(color: Colors.blue),
    Container(color: Colors.yellow),
  ],
)

主要属性

  • crossAxisCount:列数。
  • mainAxisSpacing:主轴(垂直)方向间距。
  • crossAxisSpacing:交叉轴(水平)方向间距。
  • childAspectRatio:子控件的宽高比。
  • children:子控件列表。

ListView

描述:可滚动的列表,用于按顺序显示子控件。

用法

ListView.builder(
  itemCount: 100,
  itemBuilder: (context, index) {
    return ListTile(
      leading: Icon(Icons.person),
      title: Text('用户 $index'),
      subtitle: Text('用户详情'),
      trailing: Icon(Icons.arrow_forward),
    );
  },
)

主要属性

  • children:子控件列表。
  • itemCount:列表项数量(用于 builder 模式)。
  • itemBuilder:列表项构建器函数。

输入控件

TextField

描述:用于文本输入的控件,支持单行和多行输入。

用法

TextField(
  decoration: InputDecoration(
    labelText: '用户名',
    hintText: '请输入用户名',
    prefixIcon: Icon(Icons.person),
  ),
  onChanged: (value) {
    print('输入的内容:$value');
  },
)

主要属性

  • decoration:输入框装饰,使用 InputDecoration
  • onChanged:内容改变时的回调。
  • controller:控制器,用于获取和设置文本。
  • keyboardType:键盘类型,如 TextInputType.emailAddress
  • obscureText:是否隐藏输入的内容(如密码)。
  • maxLength:最大输入长度。

Checkbox

描述:复选框,可用于选择或取消选择某个选项。

用法

Checkbox(
  value: _isChecked,
  onChanged: (bool? newValue) {
    setState(() {
      _isChecked = newValue!;
    });
  },
)

主要属性

  • value:当前选中状态。
  • onChanged:状态改变时的回调。
  • activeColor:选中时的颜色。

Radio

描述:单选按钮,用于从多个选项中选择一个。

用法

Radio<int>(
  value: 1,
  groupValue: _selectedValue,
  onChanged: (int? newValue) {
    setState(() {
      _selectedValue = newValue!;
    });
  },
)

主要属性

  • value:当前单选按钮的值。
  • groupValue:当前组中被选中的值。
  • onChanged:选中状态改变时的回调。

Switch

描述:开关控件,用于在开和关之间切换。

用法

Switch(
  value: _isSwitched,
  onChanged: (bool newValue) {
    setState(() {
      _isSwitched = newValue;
    });
  },
  activeColor: Colors.green,
)

主要属性

  • value:当前状态。
  • onChanged:状态改变时的回调。
  • activeColor:打开时的颜色。

Slider

描述:滑块控件,用于在给定范围内选择一个值。

用法

Slider(
  value: _sliderValue,
  min: 0.0,
  max: 100.0,
  divisions: 10,
  label: '${_sliderValue.round()}',
  onChanged: (double newValue) {
    setState(() {
      _sliderValue = newValue;
    });
  },
)

主要属性

  • value:当前值。
  • minmax:最小值和最大值。
  • divisions:将滑块分成的等份数。
  • label:显示在滑块上的标签。
  • onChanged:值改变时的回调。

Form

描述:表单,用于对多个输入控件进行统一管理和验证。

用法

final _formKey = GlobalKey<FormState>();

Form(
  key: _formKey,
  child: Column(
    children: <Widget>[
      TextFormField(
        decoration: InputDecoration(labelText: '邮箱'),
        validator: (value) {
          if (value == null || value.isEmpty) {
            return '请输入邮箱';
          }
          return null;
        },
      ),
      ElevatedButton(
        onPressed: () {
          if (_formKey.currentState!.validate()) {
            // 表单验证通过
          }
        },
        child: Text('提交'),
      ),
    ],
  ),
)

主要属性

  • key:用于标识表单的全局键。
  • child:表单的子控件。
  • autovalidateMode:自动验证模式。

DropdownButton

描述:下拉按钮,可以从多个选项中选择一个。

用法

String _selectedItem = '选项1';

DropdownButton<String>(
  value: _selectedItem,
  items: <String>['选项1', '选项2', '选项3']
      .map<DropdownMenuItem<String>>((String value) {
    return DropdownMenuItem<String>(
      value: value,
      child: Text(value),
    );
  }).toList(),
  onChanged: (String? newValue) {
    setState(() {
      _selectedItem = newValue!;
    });
  },
)

主要属性

  • value:当前选中的值。
  • items:可供选择的菜单项列表。
  • onChanged:选项改变时的回调。

按钮控件 (Button Widgets)

ElevatedButton

  • 概述: ElevatedButton 是一个带有凸起效果的按钮,用户点击时会有立体感。

  • 常见属性:

    • onPressed: 按钮点击事件的回调函数,若为 null 则按钮不可用。
    • child: 按钮中的内容,通常为 TextIcon
    • style: 自定义按钮的样式,如背景颜色、阴影、边框等。

    示例:

    ElevatedButton(
      onPressed: () {},
      child: Text('Elevated Button'),
    )
    

TextButton

  • 概述: TextButton 是一个没有边框和背景的文本按钮,适合用于轻量级的按钮需求。

  • 常见属性:

    • onPressed: 点击时触发的回调。
    • child: 按钮的内容,通常为文本。
    • style: 可定制字体颜色、内边距等。

    示例:

    TextButton(
      onPressed: () {},
      child: Text('Text Button'),
    )
    

OutlinedButton

  • 概述: OutlinedButton 是一个带边框但没有背景的按钮,适合用于较轻的交互需求。

  • 常见属性:

    • onPressed: 点击事件的回调。
    • child: 按钮内容。
    • style: 自定义边框颜色、形状等。

    示例:

    OutlinedButton(
      onPressed: () {},
      child: Text('Outlined Button'),
    )
    

IconButton

  • 概述: IconButton 是一个点击响应的图标按钮,可以用来触发特定的功能,如导航、删除等。

  • 常见属性:

    • icon: 显示的图标。
    • onPressed: 点击时触发的回调。
    • tooltip: 长按按钮时显示的提示文字。

    示例:

    IconButton(
      icon: Icon(Icons.add),
      onPressed: () {},
    )
    

FloatingActionButton

  • 概述: FloatingActionButton 是一个悬浮的圆形按钮,通常用于页面的主要交互操作。

  • 常见属性:

    • onPressed: 按钮点击的回调函数。
    • child: 按钮中的图标或文本。
    • backgroundColor: 背景颜色。

    示例:

    FloatingActionButton(
      onPressed: () {},
      child: Icon(Icons.add),
    )
    

PopupMenuButton

  • 概述: PopupMenuButton 是一个点击后弹出菜单项的按钮,适合用于选项选择或菜单操作。

  • 常见属性:

    • onSelected: 选择某个菜单项后的回调。
    • itemBuilder: 构建弹出的菜单项列表。

    示例:

    PopupMenuButton<String>(
      onSelected: (value) {
        // Handle selection
      },
      itemBuilder: (BuildContext context) {
        return ['Option 1', 'Option 2'].map((String choice) {
          return PopupMenuItem<String>(
            value: choice,
            child: Text(choice),
          );
        }).toList();
      },
    )
    

导航控件 (Navigation Widgets)

AppBar

  • 概述: AppBar 是一个放置在 Scaffold 顶部的导航栏控件,通常包含标题、导航图标、操作按钮等。

  • 常见属性:

    • title: 导航栏的标题。
    • actions: 导航栏右侧的操作按钮,如搜索、分享等。
    • leading: 导航栏左侧的控件,通常为返回按钮或菜单按钮。

    示例:

    AppBar(
      title: Text('App Bar'),
      actions: [
        IconButton(
          icon: Icon(Icons.search),
          onPressed: () {},
        ),
      ],
    )
    

BottomNavigationBar

  • 概述: BottomNavigationBar 是页面底部的导航栏,通常用于在多个页面之间切换。

  • 常见属性:

    • items: 导航栏中的菜单项,通常为 BottomNavigationBarItem 的列表。
    • currentIndex: 当前选中的菜单项索引。
    • onTap: 点击某个菜单项时触发的回调。

    示例:

    BottomNavigationBar(
      currentIndex: 0,
      onTap: (index) {
        // Handle tab change
      },
      items: [
        BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
        BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
      ],
    )
    

Drawer

  • 概述: Drawer 是一个从屏幕边缘滑出的侧边栏,通常用于显示导航选项或设置菜单。

  • 常见属性:

    • child: 侧边栏中的内容,通常为导航列表。

    示例:

    Drawer(
      child: ListView(
        children: [
          DrawerHeader(child: Text('Header')),
          ListTile(
            title: Text('Item 1'),
            onTap: () {},
          ),
        ],
      ),
    )
    

TabBar

  • 概述: TabBar 是用于页面标签切换的控件,通常与 TabBarView 一起使用。

  • 常见属性:

    • tabs: Tab 标签项的列表。
    • controller: 控制 Tab 页的切换。

    示例:

    TabBar(
      tabs: [
        Tab(text: 'Tab 1'),
        Tab(text: 'Tab 2'),
      ],
    )
    

TabBarView

  • 概述: TabBarView 是与 TabBar 配合使用的内容视图,当标签切换时显示不同的内容。

  • 常见属性:

    • children: 每个 Tab 对应的内容视图。
    • controller: 控制切换的控制器。

    示例:

    TabBarView(
      children: [
        Center(child: Text('Tab 1 Content')),
        Center(child: Text('Tab 2 Content')),
      ],
    )
    

Navigator

  • 概述: Navigator 是 Flutter 中管理路由的控件,提供页面间导航的能力。

  • 常见方法:

    • push: 推入一个新页面。
    • pop: 返回上一个页面。

    示例:

    Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => NewPage()),
    );
    

PageView

  • 概述: PageView 是一个可以水平或垂直滑动的多页面视图控件,常用于图片轮播或页面翻阅。

  • 常见属性:

    • children: 子页面列表。
    • controller: 控制滑动的控制器。

    示例:

    PageView(
      children: [
        Container(color: Colors.red),
        Container(color: Colors.blue),
      ],
    )
    

动画与过渡控件 (Animation and Transition Widgets)

AnimatedContainer

  • 概述: AnimatedContainer 是一个带有动画效果的容器,属性的变化会触发过渡动画。

  • 常见属性:

    • duration: 动画持续时间。
    • curve: 动画曲线。

    示例:

    AnimatedContainer(
      duration: Duration(seconds: 1),
      color: Colors.blue,
      height: 100.0,
      width: 100.0,
    )
    

AnimatedOpacity

  • 概述: AnimatedOpacity 用于对不透明度的变化进行动画处理,适合用于渐隐渐现效果。

  • 常见属性:

    • opacity: 不透明度值。
    • duration: 动画持续时间。

    示例:

    AnimatedOpacity(
      opacity: 0.5,
      duration: Duration(seconds: 2),
      child: Container(color: Colors.red),
    )
    

Hero

  • 概述: Hero 用于两个页面之间的元素过渡动画,创建视觉连贯的动画效果。

  • 常见属性:

    • tag: 唯一标识符,用于在两个页面间匹配相同的控件。

    示例:

    Hero(
      tag: 'heroTag',
      child: Image.asset('path_to_image'),
    )
    

Transform

  • 概述: Transform 控件用于应用几何变换,如平移、旋转、缩放等。

  • 常见属性:

    • transform: 变换矩阵,如 Matrix4 用于平移、旋转等。

    示例:

Transform.rotate(
angle: 0.5,
child: Container(color: Colors.blue),
)


### **AnimationController**

- **概述**: AnimationController 是动画的核心控制器,用于控制动画的进度、启动、停止等。

- **常见属性与方法**:

- `vsync`: 提高动画性能。
- `duration`: 动画的持续时间。
- `forward`, `reverse`, `stop`: 启动、反转、停止动画。

**示例**:

```dart
AnimationController(
  vsync: this,
  duration: Duration(seconds: 2),
)

FadeTransition

  • 概述: FadeTransition 用于实现渐隐渐现的动画效果。

  • 常见属性:

    • opacity: 动画的不透明度控制,通常使用 Animation。

    示例:

    FadeTransition(
      opacity: animation,
      child: Text('Fade Transition'),
    )
    

ScaleTransition

  • 概述: ScaleTransition 用于实现缩放动画。

  • 常见属性:

    • scale: 动画的缩放值,通常使用 Animation。

    示例:

    ScaleTransition(
      scale: animation,
      child: Icon(Icons.star),
    )
    

SizeTransition

  • 概述: SizeTransition 根据动画的值对子控件进行尺寸缩放,适用于线性变化。

  • 常见属性:

    • sizeFactor: 尺寸因子,决定动画的变化。

    示例:

    SizeTransition(
      sizeFactor: animation,
      child: Container(color: Colors.blue),
    )
    

PositionedTransition

  • 概述: PositionedTransition 是基于 Positioned 控件实现的动画,用于动态改变子控件的 topleft 等定位属性。

  • 常见属性:

    • rect: 定位属性的动画值。

    示例:

    PositionedTransition(
      rect: animation,
      child: Text('Positioned Transition'),
    )
    

SlideTransition

  • 概述: SlideTransition 实现平移动画,通常用于滑动视图的过渡效果。

  • 常见属性:

    • position: 动画控制的滑动位置,通常为 Animation。

    示例:

    SlideTransition(
      position: animation,
      child: Text('Slide Transition'),
    )
    

1. 样式与主题控件

Theme
  • 概述: Theme 控件用于设置应用的全局样式和主题。

  • 常见属性:

    • data: 定义 ThemeData 对象,包括颜色、字体等。
    • child: 需要应用主题的子控件。

    示例:

    Theme(
      data: ThemeData(primarySwatch: Colors.blue),
      child: MyApp(),
    )
    
MediaQuery
  • 概述: MediaQuery 控件用于获取设备的屏幕尺寸、方向和其他媒体信息。

  • 常见属性:

    • data: 提供 MediaQueryData 对象,包括屏幕宽度、高度、设备像素比等。
    • child: 需要响应媒体查询的子控件。

    示例:

    MediaQuery.of(context).size.width;
    
DefaultTextStyle
  • 概述: DefaultTextStyle 设置子控件的默认文本样式。

  • 常见属性:

    • style: TextStyle 对象,指定文本颜色、字体等。
    • child: 应用样式的子控件。

    示例:

    DefaultTextStyle(
      style: TextStyle(fontSize: 20, color: Colors.red),
      child: Text('Styled Text'),
    )
    
IconTheme
  • 概述: IconTheme 用于设置图标的默认样式。

  • 常见属性:

    • data: IconThemeData 对象,定义图标的大小、颜色等。
    • child: 应用图标样式的子控件。

    示例:

    IconTheme(
      data: IconThemeData(color: Colors.green),
      child: Icon(Icons.star),
    )
    
Builder
  • 概述: Builder 用于在其子控件中构建上下文环境,以便访问 BuildContext

  • 常见属性:

    • builder: 返回需要的 Widget 的构建器函数。

    示例:

    Builder(
      builder: (context) {
        return Text('Width: ${MediaQuery.of(context).size.width}');
      },
    )
    

2. 异步与状态管理控件

StatefulWidget
  • 概述: StatefulWidget 是一个状态变化的控件,每次状态变化都会重新构建。

  • 常见属性:

    • createState: 创建控件的状态对象。

    示例:

    class MyStatefulWidget extends StatefulWidget {
      
      _MyStatefulWidgetState createState() => _MyStatefulWidgetState();
    }
    
    class _MyStatefulWidgetState extends State<MyStatefulWidget> {
      
      Widget build(BuildContext context) {
        return Container();
      }
    }
    
StatelessWidget
  • 概述: StatelessWidget 是一个无状态控件,构建时不依赖状态。

  • 常见属性:

    • build: 构建控件的方法。

    示例:

    class MyStatelessWidget extends StatelessWidget {
      
      Widget build(BuildContext context) {
        return Text('I am stateless');
      }
    }
    
FutureBuilder
  • 概述: FutureBuilder 根据异步 Future 的状态动态构建 UI。

  • 常见属性:

    • future: 需要等待的异步操作。
    • builder: 返回根据 AsyncSnapshot 构建的 Widget

    示例:

    FutureBuilder<String>(
      future: fetchData(),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
          return Text(snapshot.data ?? 'Error');
        } else {
          return CircularProgressIndicator();
        }
      },
    )
    
StreamBuilder
  • 概述: StreamBuilder 根据数据流 Stream 的状态动态构建 UI。

  • 常见属性:

    • stream: 需要监听的数据流。
    • builder: 返回根据 AsyncSnapshot 构建的 Widget

    示例:

    StreamBuilder<int>(
      stream: myStream,
      builder: (context, snapshot) {
        return Text('Stream Value: ${snapshot.data}');
      },
    )
    

滚动控件

SingleChildScrollView
  • 概述: SingleChildScrollView 允许一个子控件在可滚动的视图中显示。

  • 常见属性:

    • child: 需要滚动显示的子控件。
    • scrollDirection: 滚动方向,默认为垂直方向。

    示例:

    SingleChildScrollView(
      child: Column(
        children: List.generate(100, (index) => Text('Item $index')),
      ),
    )
    
Scrollbar
  • 概述: Scrollbar 控件用于显示滚动条。

  • 常见属性:

    • child: 可滚动的子控件。

    示例:

    Scrollbar(
      child: ListView.builder(
        itemCount: 100,
        itemBuilder: (context, index) => ListTile(title: Text('Item $index')),
      ),
    )
    
CustomScrollView
  • 概述: CustomScrollView 是一个支持自定义滚动效果的控件。

  • 常见属性:

    • slivers: 需要滚动的子控件,通常是 Sliver 类型。

    示例:

    CustomScrollView(
      slivers: [
        SliverList(
          delegate: SliverChildBuilderDelegate(
            (context, index) => ListTile(title: Text('Item $index')),
            childCount: 100,
          ),
        ),
      ],
    )
    
NotificationListener
  • 概述: NotificationListener 用于监听子控件中的滚动事件。

  • 常见属性:

    • onNotification: 处理通知事件的回调。

    示例:

    NotificationListener<ScrollNotification>(
      onNotification: (notification) {
        print(notification.metrics.pixels);
        return true;
      },
      child: ListView.builder(
        itemCount: 100,
        itemBuilder: (context, index) => ListTile(title: Text('Item $index')),
      ),
    )
    

绘制与效果控件

Opacity
  • 概述: Opacity 控件用于设置子控件的不透明度。

  • 常见属性:

    • opacity: 控件的透明度,范围为 0.0 - 1.0。

    示例:

    Opacity(
      opacity: 0.5,
      child: Text('Semi-transparent text'),
    )
    
ClipRect
  • 概述: ClipRect 用于裁剪矩形区域中的子控件。

  • 常见属性:

    • child: 需要裁剪的子控件。

    示例:

    ClipRect(
      child: Align(
        alignment: Alignment.topLeft,
        widthFactor: 0.5,
        heightFactor: 0.5,
        child: Container(color: Colors.red),
      ),
    )
    
ClipRRect
  • 概述: ClipRRect 用于裁剪圆角矩形区域中的子控件。

  • 常见属性:

    • borderRadius: 圆角半径。
    • child: 需要裁剪的子控件。

    示例:

    ClipRRect(
      borderRadius: BorderRadius.circular(16.0),
      child: Container(color: Colors.blue, width: 100, height: 100),
    )
    
ClipOval
  • 概述: ClipOval 用于裁剪椭圆形区域中的子控件。

  • 常见属性:

    • child: 需要裁剪的子控件。

    示例:

    ClipOval(
      child: Image.network('https://via.placeholder.com/150'),
    )
    
ClipPath
  • 概述: ClipPath 使用自定义路径裁剪子控件。

  • 常见属性:

    • clipper: 自定义路径裁剪器。

    示例:

    ClipPath(
      clipper: MyCustomClipper(),
      child: Container(color: Colors.green),
    )
    
CustomPaint
  • 概述: CustomPaint 用于自定义绘制内容。
  • 常见属性:
    • painter: 自定义绘制

器。

示例:

CustomPaint(
painter: MyPainter(),
child: Container(),
)

1. 交互模型

GestureDetector
  • 概述: GestureDetector 是一个无形的控件,能够检测用户的手势(如点击、拖动、滑动等)。

  • 常见属性:

    • onTap: 点击时调用的回调。
    • onDoubleTap: 双击时调用的回调。
    • onLongPress: 长按时调用的回调。
    • onPanUpdate: 拖动时调用的回调。

    示例:

    GestureDetector(
      onTap: () {
        print('Tapped!');
      },
      onLongPress: () {
        print('Long pressed!');
      },
      child: Container(
        color: Colors.blue,
        width: 100,
        height: 100,
        child: Center(child: Text('Tap Me')),
      ),
    )
    
Dismissible
  • 概述: Dismissible 控件用于实现滑动删除的功能,适合用于列表项。

  • 常见属性:

    • key: 唯一标识符,通常是 Key 类型。
    • background: 当控件被滑动时显示的背景。
    • child: 需要滑动的子控件。
    • onDismissed: 控件被滑动并删除时的回调。

    示例:

    Dismissible(
      key: Key('item'),
      background: Container(color: Colors.red),
      child: ListTile(title: Text('Swipe to delete')),
      onDismissed: (direction) {
        print('Item dismissed');
      },
    )
    
Draggable
  • 概述: Draggable 控件用于创建可拖动的控件。

  • 常见属性:

    • child: 拖动时显示的子控件。
    • feedback: 拖动时的反馈控件。
    • childWhenDragging: 拖动时原控件显示的内容。

    示例:

    Draggable(
      data: 'data',
      child: Container(color: Colors.blue, width: 100, height: 100),
      feedback: Material(
        child: Container(color: Colors.red, width: 100, height: 100),
      ),
      childWhenDragging: Container(color: Colors.grey, width: 100, height: 100),
    )
    
LongPressDraggable
  • 概述: LongPressDraggable 是一种专门用于长按拖动的控件。

  • 常见属性:

    • Draggable 类似,增加了对长按的支持。

    示例:

    LongPressDraggable(
      data: 'data',
      child: Container(color: Colors.blue, width: 100, height: 100),
      feedback: Material(
        child: Container(color: Colors.red, width: 100, height: 100),
      ),
    )
    
DragTarget
  • 概述: DragTarget 用于接收拖动控件的数据。

  • 常见属性:

    • builder: 构建拖放目标的函数,接收当前拖动状态。
    • onAccept: 当接受到拖动数据时调用的回调。

    示例:

    DragTarget<String>(
      builder: (context, candidateData, rejectedData) {
        return Container(color: Colors.green, width: 100, height: 100);
      },
      onAccept: (data) {
        print('Accepted: $data');
      },
    )
    

2. Material 组件

Scaffold
  • 概述: Scaffold 是一个 Material 设计的基本布局结构,包含应用程序的主要视觉界面元素。

  • 常见属性:

    • appBar: 顶部的应用栏。
    • body: 主要的内容区域。
    • floatingActionButton: 浮动操作按钮。
    • bottomNavigationBar: 底部导航栏。

    示例:

    Scaffold(
      appBar: AppBar(title: Text('Scaffold Example')),
      body: Center(child: Text('Hello World')),
      floatingActionButton: FloatingActionButton(onPressed: () {}),
    )
    
MaterialApp
  • 概述: MaterialApp 是一个封装了多个 Material 设计相关的配置的控件,通常作为应用程序的根控件。

  • 常见属性:

    • home: 应用的主界面。
    • theme: 应用的主题设置。

    示例:

    MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('MaterialApp Example')),
        body: Center(child: Text('Hello World')),
      ),
    )
    
Material
  • 概述: Material 是一个简单的控件,用于将子控件渲染为 Material 设计风格。

  • 常见属性:

    • child: 需要应用 Material 风格的子控件。

    示例:

    Material(
      child: Container(color: Colors.red),
    )
    
Card
  • 概述: Card 是一个 Material 风格的容器,通常用于显示内容。

  • 常见属性:

    • child: 显示在卡片上的内容。
    • elevation: 卡片的阴影深度。

    示例:

    Card(
      elevation: 4,
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Text('This is a Card'),
      ),
    )
    
Chip
  • 概述: Chip 是一个 Material 风格的小块,通常用于展示信息或选项。

  • 常见属性:

    • label: 显示的文本。
    • avatar: 显示的头像。

    示例:

    Chip(
      label: Text('Chip Label'),
      avatar: CircleAvatar(child: Text('A')),
    )
    
SnackBar
  • 概述: SnackBar 是一个短暂显示的消息提示框,通常用于显示反馈信息。

  • 常见属性:

    • content: 显示的内容。
    • action: 额外的操作按钮。

    示例:

    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text('This is a SnackBar'),
        action: SnackBarAction(label: 'Undo', onPressed: () {}),
      ),
    )
    
Dialog
  • 概述: Dialog 是一个弹出窗口,通常用于显示信息或请求用户操作。

  • 常见属性:

    • child: 显示在对话框中的内容。

    示例:

    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: Text('Dialog Title'),
        content: Text('This is a dialog'),
        actions: [TextButton(onPressed: () => Navigator.of(context).pop(), child: Text('OK'))],
      ),
    );
    
AlertDialog
  • 概述: AlertDialog 是一种特殊类型的对话框,用于提示用户重要信息或请求确认。

  • 常见属性:

    • title: 对话框的标题。
    • content: 对话框的内容。

    示例:

    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: Text('Alert Title'),
        content: Text('This is an alert dialog.'),
        actions: [
          TextButton(
            onPressed: () => Navigator.of(context).pop(),
            child: Text('Cancel'),
          ),
          TextButton(
            onPressed: () => Navigator.of(context).pop(),
            child: Text('OK'),
          ),
        ],
      ),
    );
    
SimpleDialog
  • 概述: SimpleDialog 是一个简单的对话框,通常用于提供多个选项供用户选择。

  • 常见属性:

    • title: 对话框的标题。
    • children: 显示在对话框中的子控件。

    示例:

    showDialog(
      context: context,
      builder: (context) => SimpleDialog(
        title: Text('Choose an option'),
        children: [
          SimpleDialogOption(child: Text('Option 1'), onPressed: () {}),
          SimpleDialogOption(child: Text('Option 2'), onPressed: () {}),
        ],
      ),
    );
    
BottomSheet
  • 概述: BottomSheet 是从屏幕底部滑出的面板,可以用于显示额外内容。

  • 常见属性:

    • builder: 返回底部面板的构建器函数。

    示例:

    showModalBottomSheet(
      context: context,
      builder: (context) => Container(
        height:
    

200,
child: Center(child: Text(‘This is a BottomSheet’)),
),
);

#### **ExpansionPanel**
- **概述**: `ExpansionPanel` 是一种可展开的面板,适合用于显示可折叠的信息。
- **常见属性**:
- `headerBuilder`: 用于构建面板头部的函数。
- `body`: 显示在面板展开时的内容。

**示例**:
```dart
ExpansionPanelList(
  expandedHeaderAlignment: ExpansionPanelHeaderAlignment.center,
  elevation: 1,
  expandedPanel: true,
  children: [
    ExpansionPanel(
      headerBuilder: (context, isExpanded) => ListTile(title: Text('Panel Header')),
      body: ListTile(title: Text('This is the body')),
      isExpanded: true,
    ),
  ],
)

3. Cupertino(iOS 风格)控件

CupertinoApp
  • 概述: CupertinoApp 是一个应用程序的根控件,具有 iOS 风格的外观和行为。

  • 常见属性:

    • home: 应用的主界面。

    示例:

    CupertinoApp(
      home: CupertinoPageScaffold(
        navigationBar: CupertinoNavigationBar(
          middle: Text('Cupertino App'),
        ),
        child: Center(child: Text('Hello World')),
      ),
    );
    
CupertinoButton
  • 概述: CupertinoButton 是一个 iOS 风格的按钮。

  • 常见属性:

    • onPressed: 按钮被点击时的回调。
    • child: 按钮内部显示的内容。

    示例:

    CupertinoButton(
      onPressed: () {
        print('Button pressed');
      },
      child: Text('Click Me'),
    )
    
CupertinoNavigationBar
  • 概述: CupertinoNavigationBar 是一个 iOS 风格的导航栏,通常放在页面顶部。

  • 常见属性:

    • middle: 中间显示的标题。
    • leading: 导航栏左侧的控件。

    示例:

    CupertinoNavigationBar(
      middle: Text('Navigation Bar'),
      leading: CupertinoButton(
        padding: EdgeInsets.zero,
        child: Icon(CupertinoIcons.back),
        onPressed: () {},
      ),
    )
    
CupertinoTabScaffold
  • 概述: CupertinoTabScaffold 是一个 iOS 风格的标签式页面结构。

  • 常见属性:

    • tabBar: 显示在底部的标签栏。
    • tabBuilder: 用于构建每个标签对应的页面。

    示例:

    CupertinoTabScaffold(
      tabBar: CupertinoTabBar(
        items: [
          BottomNavigationBarItem(icon: Icon(CupertinoIcons.home), label: 'Home'),
          BottomNavigationBarItem(icon: Icon(CupertinoIcons.settings), label: 'Settings'),
        ],
      ),
      tabBuilder: (context, index) {
        return Center(child: Text('Tab $index'));
      },
    )
    
CupertinoAlertDialog
  • 概述: CupertinoAlertDialog 是一种 iOS 风格的警告对话框。

  • 常见属性:

    • title: 对话框的标题。
    • content: 对话框的内容。
    • actions: 显示在对话框底部的操作按钮。

    示例:

    showCupertinoDialog(
      context: context,
      builder: (context) => CupertinoAlertDialog(
        title: Text('Alert'),
        content: Text('This is an alert dialog'),
        actions: [
          CupertinoDialogAction(
            onPressed: () => Navigator.of(context).pop(),
            child: Text('OK'),
          ),
        ],
      ),
    );
    

这份详细的控件描述涵盖了 Flutter 中的一些主要控件及其用法,帮助你更好地理解和使用 Flutter 进行应用开发!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/897235.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

iTOP-RK3568开发板独立NPU通过算法加特应用到以下的场景

iTOP-3568开发板采用瑞芯微RK3568处理器&#xff0c;内部集成了四核64位Cortex-A55处理器。主频高达2.0Ghz&#xff0c;RK809动态调频。集成了双核心架构GPU&#xff0c;ARM G52 2EE、支持OpenGLES1.1/2.0/3.2、OpenCL2.0、Vulkan1.1、内嵌高性能2D加速硬件。 内置独立NPU,算力…

2024软考网络工程师笔记 - 第10章.组网技术

文章目录 交换机基础1️⃣交换机分类2️⃣其他分类方式3️⃣级联和堆叠4️⃣堆叠优劣势5️⃣交换机性能参数 &#x1f551;路由器基础1️⃣路由器接口2️⃣交换机路由器管理方式2️⃣交换机路由器管理方式 交换机基础 1️⃣交换机分类 1.根据交换方式分 存储转发式交换(Store…

信息搜集 ---开发框架识别

开发框架识别 插件推荐 插件商店搜索wappalyzer Python - Django&Flask Django 1、wappalyzer插件 2、返回数据包的特征字段 Set-Cookie:expires Flask 1、wappalyzer插件 2、返回数据包的特征字段 Set-Cookie:expires 或 Etag: "flask PHP - ThinkPHP&Lar…

Rust小练习,编写井字棋

画叉画圈的游戏通常指的是 井字棋&#xff08;Tic-Tac-Toe&#xff09;&#xff0c;是一个简单的两人游戏&#xff0c;规则如下&#xff1a; 游戏规则 棋盘&#xff1a;游戏在一个3x3的方格上进行。玩家&#xff1a;有两个玩家&#xff0c;一个用“X”表示&#xff0c;另一个…

springboot基于微信小程序的企业考勤系统设计与实现

文章目录 前言项目介绍技术介绍功能介绍核心代码数据库参考 系统效果图文章目录 前言 文章底部名片&#xff0c;获取项目的完整演示视频&#xff0c;免费解答技术疑问 项目介绍 伴随着我国社会的发展&#xff0c;人民生活质量日益提高。于是对各种需求进行规范而严格是十分有…

单链表的建立

步骤&#xff1a; 1.初始化一个单链表 2.每次取一个数据元素&#xff0c;插到表头或者表尾 尾插法建立单链表 头插法建立单链表: 养成好习惯&#xff0c;只要是初始化单链表&#xff0c;都先把头指针指向NULL。 重要应用&#xff1a;单链表的逆置 头插法&#xff0c;尾插…

C++笔记之类三种的继承方式

C++笔记之类三种的继承方式 code review! 文章目录 C++笔记之类三种的继承方式1.《C++ Primer Plus》(第6版)中文版Page 5502.C++类继承方式与能否隐式向上转换的关系1.《C++ Primer Plus》(第6版)中文版Page 550 除基类私有成员变量外(基类公有成员变量和保护成员变量):…

Java 虚拟机实战(基础篇 1万字)

此笔记来自于黑马程序员 基础篇 初识 JVM(Java Virtual Machine) 什么是 JVM JVM 本质上是一个运行在计算机上的程序&#xff0c;他的职责是运行 Java 字节码文件 JVM 的功能 翻译成字节码 即时编译 Java语言如果不做任何优化&#xff0c;性能不如C、C等语言。Java 支持跨…

【Linux】-权限

&#x1f511;&#x1f511;博客主页&#xff1a;阿客不是客 &#x1f353;&#x1f353;系列专栏&#xff1a;深入代码世界&#xff0c;了解掌握 Linux 欢迎来到泊舟小课堂 &#x1f618;博客制作不易欢迎各位&#x1f44d;点赞⭐收藏➕关注 ​ 一、权限的概念 在Linux 中&…

“新物种”即将上线,极氪MIX是近几年最“好玩”的新车?

像极氪MIX这样有创意的新能源车 除了概念车外&#xff0c;市面上真的很少能看到类似的量产车 别致可爱的造型、新颖的对开门设计、百变的空间布局 同时兼顾了MPV大空间以及SUV的操控乐趣和通过性 妥妥的“新物种” A级车车长D级车轴距&#xff0c;配合隐藏式双B柱电动对开…

【uniapp+Typescript】300行代码手撸了一个多端图片比较组件

今天刚新鲜出炉的。DCloud市场上的看了下&#xff0c;都不好用&#xff0c;于是自己撸了个。基于unibestccframe框架。 用户图片对比&#xff0c;支持滑块拖动对比、图片放大缩小、下载这些基本功能。 左右对比模式还没写&#xff0c;等什么时候想弄了&#xff0c;再来更新。…

化繁为简,使用 ADManager Plus 简化账户生命周期管理

在 IT 环境中&#xff0c;编排指的是对工作流、应用程序和系统的协调管理&#xff0c;旨在通过简化流程来优化业务性能。IT 管理员可以通过编排&#xff0c;从单个控制台自动执行一系列任务&#xff0c;例如预配账户帐户、数据库管理、事件处理、应用程序和云资源管理。编排对支…

第23章 - Elasticsearch 洞悉你的查询:如何在上线前发现潜在问题!

文章目录 1. 前言2. Profile API - 查询优化2.1 Profile API 简单介绍2.2 查询结果图形化2.3 Profile 注意事项 3. Explain API - 解释查询 1. 前言 在第 21 章中&#xff0c;我介绍了 Elasticsearch 的读优化&#xff0c;但你是否曾疑惑&#xff1a;如何在上线前判断查询的耗…

springboot基于Java的民宿山庄农家乐系统设计与实现

文章目录 前言项目介绍技术介绍功能介绍核心代码数据库参考 系统效果图文章目录 前言 文章底部名片&#xff0c;获取项目的完整演示视频&#xff0c;免费解答技术疑问 项目介绍 当今社会已经步入了科学技术进步和经济社会快速发展的新时期&#xff0c;国际信息和学术交流也不…

Mojo在Windows上详细安装步骤

Mojo官方文档是基于Linux写的&#xff0c;在Windows上基于WSLUbuntu安装还有些细节问题需要注意&#xff0c;完整安装步骤整理如下&#xff1a; 1.Windows版本必须是Windows10以上&#xff0c;而且版本≥1903&#xff0c;或者内部版本≥18362&#xff0c;若不满足&#xff0c;…

渗透测试实战—教育攻防演练中突破网络隔离

免责声明&#xff1a;文章来源于真实渗透测试&#xff0c;已获得授权&#xff0c;且关键信息已经打码处理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本…

【redis】热点key问题

【redis】热点key问题 【一】什么是热点key问题【二】什么样的key被称为热key【三】热点Key问题的危害【四】如何监控发现热点key【五】热点Key的解决方案【1】使用二级缓存【2】将热key分散到不同的服务器中【3】热key拆分【4】将核心/非核心业务做Redis的隔离 【六】业界已有…

C语言指针(1)

指针一句话就是存储地址的一个变量&#xff0c;当你想要拿到一个地址就需要用到&运算符。 如果要拿数组的地址就不用&运算符&#xff0c;因为数组名就是数组首元素的地址。 int main() {int pa0;int* p&pa;int arr[3]{1,2,3};int* qarr;printf("%d",*p)…

【linux】线程 (三)

13. 常见锁概念 &#xff08;一&#xff09;了解死锁 死锁是指在一组进程中的各个进程均占有不会释放的资源&#xff0c;但因互相申请被其他进程占有的&#xff0c;且不释放的资源&#xff0c;而处于的一种永久等待状态 &#xff08;二&#xff09;死锁四个必要条件 互斥条件…

基准线markLine的值超过坐标轴范围导致markline不显示

解决问题&#xff1a;动态设置yAxis的max值&#xff08;解决基准线不在y轴范围&#xff09; yAxis: [{name: 单位&#xff1a;千,...yAxis,nameTextStyle:{...yAxis.nameTextStyle,padding: [0,26,0,24]},paddingLeft:24,paddingRight:26},{name: 单位&#xff1a;百分比,...yA…