Flutter介绍


Flutter横跨Android、iOS、MacOS、Windows、Linux等多个系统,还可以打包成Web程序运行在浏览器上。

1 环境搭建

  • 虚拟机下Win10企业版(Win10-1909-Business-Editions-DVD-x64)、Chrome。
  • 下载安装Flutter依赖的命令行工具Git For Windows:Git-2.37.1-64-bit.exe
  • Flutter SDK下载:Flutter-Windows-3.0.5-Stable.zip
    • 解压到指定位置,找到文件夹中的flutter_console.bat文件,双击运行。
    • 注意:不要将Flutter SDK安装到高权限路径下,例如C盘路径(系统盘)。
  • 添加环境变量:控制面板—>用户账户—>更改我的环境变量—>环境变量。
    • 新建变量名PUB_HOSTED_URL:变量值https://pub.flutter-io.cn/
    • 新建变量名FLUTTER_STORAGE_BASE_URL:变量值https://storage.flutter-io.cn/
    • 变量Path中添加:变量值C:\Program\Flutter\bin(Flutter SDK安装路径的bin目录)。

1-1 编辑器的安装

  • 安装Visual Studio:VisualStudioSetup.exe,使用C++的桌面工具。
    • 勾选用于Windows的C++ CMake工具
    • 勾选Windows 10 SDK(10.0.19041.0)
    • 勾选MSVC v143-VS 2022 C++ x64/86生成工具(最新)
  • 安装Android Studio:Android-Studio-2021.2.1.15-Windows.exe,可能需要使用梯子。
    • 添加环境变量ANDROID_SDK_HOME:变量值C:\Program\Avd (即模拟器存放的位置)。
    • 设置Android模拟器:虚拟机貌似无法启动模拟器,建议将环境搭建在Pc主机上操作。
      • Android Studio—>More Actions—>Virtual Device Manager—>Create device。
      • Phone·Pixel—>Next—>镜像 R—>Download—>Next—>验证配置—>Finish。
    • 安装插件:Android Studio—>Plugins—>Marketplace—>搜Flutter和Dart—>Install。
      • Flutter插件:支持Flutter开发工作流(运行、调试、热重载等)。
      • Dart插件:提供代码分析(输入代码时进行验证、代码补全等)。

1-2 flutter doctor

  • 文件夹下鼠标右键Git Bash Here,命令窗口模式输入flutter doctor进行查看。
  • cmdline-tools component is missing:Android Studio缺少cmdline-tools工具。
    • 启动Android Studio—>More Actions—>SDK Manager—>SDK Tools。
    • Android SDK Command-line Tools (latest)—>Apply—>Finish—>OK。
  • Android license status unknown.:说明该环境需要添加Android license。
    • 搜索cmd,以管理员身份运行,命令窗口下执行flutter doctor --android-licenses
    • 在提示是否接受许可时,输入y进行回车确认,最后重新输入flutter doctor进行查看。
$ flutter doctor
Flutter assets will be downloaded from https://storage.flutter-io.cn/. 
Make sure you trust this source!
Doctor summary (to see all details, run flutter doctor -v):
[] Flutter 
    (Channel stable, 3.0.5, on Microsoft Windows [版本 10.0.18363.418], locale zh-CN)
[!] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
    X cmdline-tools component is missing
      Run `path/to/sdkmanager --install "cmdline-tools;latest"`
      See https://developer.android.com/studio/command-line for more details.
    X Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/docs/get-started/install/windows
      # android-setup for more details.
[] Chrome - develop for the web
[] Visual Studio - develop for Windows (Visual Studio Community 2022 17.3.0)
[] Android Studio (version 2021.2)
[] Connected device (2 available)
[] HTTP Host Availability

! Doctor found issues in 1 categories.

2 创建项目

  • 创建项目
    • 启动Android Studio—>New Flutter Project—>Flutter—>Flutter SDK path即Flutter SDK的安装路径。
    • Next—>Project Name为myflutter(Organization包名)—>Finish—>Create—>等待打开创建的项目。
  • 想要在该项目上运行程序:File—>Open—>定位到该项目目录的Android目录—>选中—>OK—>将其打开。
  • Sync project with Gradle Files下载:在项目打开Android目录时便自动下载(位于Android Studio->File中)。
  • No Device Selected:找不到运行设备,两种调试方式(命令窗口下flutter devices可检测当前可用设备)。
    • Android真机调试
      • 手机使用USB数据线连接电脑,开启调试模式,Android Studio安装与手机对应的SDK版本。
      • Android Studio—>Tools—>SDK Manager—>SDK Platforms—>勾选对应版本SDK—>OK。
      • 当No Device Selected变为对应手机型号名称时,Run—>Run‘app’即可在手机中运行程序。
    • 模拟器调试:Android Studio—>Tools—>Device Manager—>启动之前设置好的Android模拟器。
  • 多台设备同时进行兼容性调试,命令窗口进入对应项目myflutter中,输入flutter run -d all执行程序。
  • 改完代码之后,不需要重新输入命令执行程序,以下通用快捷键可帮助日常开发。
    • 使用热加载Shift + Rr,重新加载程序。
    • 若热加载没生效,则使用命令R进行热重启。
    • 命令p显示网格,可以很好地掌握布局情况。
    • 命令o支持切换Android以及iOS的预览模式。
    • 命令q退出调试预览模式,相当于Ctrl + C

3 简单介绍

  • 创建Flutter项目的两种方式:Android Studio(正式项目建议用)、命令flutter create <project-name>
  • 目录结构
    • test:用于存放测试代码。
    • android、ios、web、windows等:各个平台的资源文件。
    • lib:flutter相关代码,开发的所有代码都放在该目录下。
    • pubspec.yaml:存放项目第三方依赖、版本号和配置信息等。
    • analysis_options.yaml:分析Dart语法的文件,老项目升级新项目时如果有警告信息,可以删除此文件。

3-1 入口方法

import 'package:flutter/material.dart';                 // 引入material主题

void main() {                                           // 入口方法
  runApp(const Center(                                  // Center组件,让内容居中
    child: Text('Hello Flutter!',                       // 查看底层代码使用“Ctrl+鼠标左键”
      textDirection: TextDirection.ltr,                 // ltr从右向左
      style: TextStyle(                                 // 改变文本样式
        // color: Colors.red,
        color: Color.fromRGBO(80, 120, 35, 1),
        fontSize: 27,
      ),
    ),
  ),);
}

3-2 必须组件

  • 开发中必须使用的装饰组件:MaterialApp(一般作为顶层Widget使用)、Scaffold(布局结构的基本实现)。
  • MaterialApp组件的常用属性:home(主页)、title(标题)、color(颜色)、theme(主题)、routes(路由)等。
  • Scaffold组件的常用属性
    • appBar:显示在界面顶部的标题栏。
    • body:当前界面所显示的主要内容。
    • drawer:抽屉菜单控件,例如左拉、右拉、弹出对话框等。
import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('必须组件')),
      body: const Center(
        child: Text('Hello Flutter!',
          textDirection: TextDirection.ltr,
          style: TextStyle(
            color: Colors.red,
            // color: Color.fromRGBO(80, 120, 35, 1),
            fontSize: 27,
          ),
        ),
      ),
    ),
  ),);
}

3-3 内容抽离

  • Flutter自定义组件即一个类,需集成StatelessWidget或StatefulWidget。
  • StatelessWidget是无状态组件,状态不可变的Widget,前期接触比较多。
  • StatefulWidget是有状态组件,持有的状态可能在Widget生命周期中改变。
import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('内容抽离')),
      body: const MyApp(),                              // 调用MyApp组件
    ),
  ),);
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}): super(key: key);             // 常量构造函数

  
  Widget build(BuildContext context) {
    return const Center(
      child: Text('Flutter自定义组件',
        textDirection: TextDirection.ltr,
        style: TextStyle(
          // color: Colors.red,
          color: Color.fromRGBO(80, 120, 35, 1),
          fontSize: 27,
        ),
      ),
    );
  }
}

4 基本结构

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('基本结构')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return const Center(
      child: Text('Hello Flutter!'),
    );
  }
}

4-1 容器组件

  • 容器组件:Container,与HTML中的div标签相似,主要用于布局。
    • decoration(装饰容器,例如:给容器添加背景或边框等)。
    • margin(和容器外部的间距)、padding(和容器内部的间距)。
    • width(容器宽度)、height(容器高度)、child(容器子元素)。
    • alignment(调整容器中组件的位置)、transform(让容器进行位移、缩放、旋转等)。
import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('容器组件')),
      // body: const MyApp(),
      body: Column(
        children: const [
          MyApp(),
          MyButton(),
        ],
      ),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        // alignment: Alignment.topLeft,                // 配置容器内元素方位,center居中
        alignment: Alignment.center,                    // topLeft左上,bottomRight右下
        margin: const EdgeInsets.fromLTRB(0, 60, 0, 0), // 配置容器外部间距,设置顶部高度
        width: 270,                                     // 配置容器宽度
        height: 270,                                    // 配置容器高度
        transform: Matrix4.translationValues(0, 3, 0),  // 沿着y轴向下平移3
        // transform: Matrix4.rotationZ(0.2),           // 沿着Z轴旋转0.2
        // transform: Matrix4.skewY(10),                // 沿着Y轴倾斜缩放10
        decoration: BoxDecoration(
          color: Colors.green,                          // 配置容器的背景颜色
          border: Border.all(                           // 配置边框颜色和宽度
            color: Colors.purple,
            width: 3,
          ),
          borderRadius: BorderRadius.circular(25),      // 配置容器圆角,100可实现圆形
          boxShadow: const [                            // 配置容器阴影效果
            BoxShadow(
              color: Colors.blue,
              blurRadius: 30.0,
            ),
          ],
          gradient: const LinearGradient(               // 配置容器渐变颜色
            colors: [                                   // LinearGradient背景线性渐变
              Colors.green,                             // RadialGradient径向渐变
              Colors.yellow,               
            ],
          ),
        ),
        child: const Text('容器组件', 
          style: TextStyle(
            color: Colors.white,
            fontSize: 66,
          ),
        ),
      ),
    );
  }
}

class MyButton extends StatelessWidget {                // 创建一个自定义的按钮
  const MyButton({Key? key}): super(key: key);

  
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      width: 200,
      height: 50,
      // margin: const EdgeInsets.all(20),              // 配置MyApp与MyButton边距
      margin: const EdgeInsets.fromLTRB(0, 60, 0, 0),
      // padding: const EdgeInsets.all(5),              // 可替换alignment
      // padding: const EdgeInsets.fromLTRB(70, 0, 0, 0),
      decoration: BoxDecoration(
        color: Colors.blue,
        borderRadius: BorderRadius.circular(20),        // 配置按钮圆角
      ),
      child: const Text('按钮', 
        style: TextStyle(
          color: Colors.white,
          fontSize: 30,
        ),
      ),
    );
  }
}

4-2 文本组件

  • style(文本样式,参数如下)、maxLines(文本显示的最大行数)。
    • color(文字颜色)、fontWeight(字体粗细:bold粗体、normal正常体)。
    • fontSize(文字大小)、fontStyle(文字样式:italic斜体、normal正常体)。
    • wordSpacing(单词间隙:负值则单词变紧凑)、letterSpacing(字母间隙:负值则字母变紧凑)。
    • decoration(文字装饰线:none无线、lineThrough删除线、overline上划线、underline下划线)。
    • decorationColor(文字装饰线颜色:red红、orange橙、yellow黄、green绿、cyan青、blue蓝、purple紫)。
    • decorationStyle(文字装饰线风格:[dashed/dotted]虚线、double两根线、solid一根实线、wavy波浪线)。
  • overflow(文本超出屏幕后的处理方式:clip裁剪、fade渐渐隐藏、ellipsis省略号)。
  • textAlign(文本对齐方式:center居中、left左对齐、right右对齐、justfy两端对齐)。
  • textScaleFactor(字体缩放)、textDirection(文本方向:ltr从左至右、rtl从右至左)。
import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('文本组件')),
      body: const MyText(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return Container(
      width: 200,
      height: 200,
      margin: const EdgeInsets.all(100),
      decoration: const BoxDecoration(
        color: Colors.yellow,
      ),
      child: const Text('纸上得来终觉浅,绝知此事要躬行。—— 冬夜读书示子聿 ▪ 陆游',
        textAlign: TextAlign.left,                      // 文本对齐方式
        overflow: TextOverflow.ellipsis,                // 溢出显示隐藏点
        maxLines: 1,                                    // 文本显示最大行数
        style: TextStyle(
          fontSize: 20,                                 // 字体大小20
          fontWeight: FontWeight.w900,                  // 字体加粗900
          color: Colors.red,                            // 字体颜色
          fontStyle: FontStyle.italic,                  // 字体倾斜
          letterSpacing: 2,                             // 字体间距
          decoration: TextDecoration.underline,         // 下划线
          decorationColor: Colors.blue,                 // 下划线黑色
          decorationStyle: TextDecorationStyle.wavy,    // 波浪线
        ),
      ),
    );
  }
}

4-3 图片组件

  • Image.network:加载远程图片。
    • alignment(图片对齐方式)、fit(依父容器控制图片的拉伸)、repeat(横纵或整个画布平铺)。
    • color和colorBlendMode(图片的背景颜色,通常两者配合使用,即图片颜色与背景色混合)。
    • width(图片宽度,结合ClipOval才能看到效果)、height(高度,结合ClipOval才能看到效果)。
  • Image.asset:加载本地图片,需在项目的根目录中新建images文件夹(放置本地图片),并修改配置文件。
# 例如:myflutter(项目名)目录结构如下
# - images/
#   - a.jpg
#   - 2.0x/
#     - b.jpg
#   - 3.0x/
#     - c.jpg
# - pubspec.yaml

# 修改pubspec.yaml配置文件内容
assets:
  - images/a.jpg
  - images/2.0x/b.jpg
  - images/3.0x/c.jpg

(1) 加载远程图片

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('加载远程图片')),
      // body: const MyApp(),
      body: Column(
        children: const [
          MyApp(),
          SizedBox(height: 5),                          // 设置两容器间距
          MyCircle(),
        ],
      ),
    ),
  ),);
}

/*
图片地址:http://gg.gg/13jnaa、http://gg.gg/13kuon、http://gg.gg/13kuow
*/

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

  
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        margin: const EdgeInsets.fromLTRB(0, 5, 0, 0),
        height: 300,
        width: 200,
        decoration: const BoxDecoration(
          color: Colors.yellow,
        ),
        child: Image.network(
          'http://gg.gg/13jnaa',
          // alignment: Alignment.centerLeft,           // 图片向左居中
          // scale: 2,                                  // 图片缩小一倍
          // fit: BoxFit.fill,                          // 图片充满容器,可能会压缩变形
          fit: BoxFit.cover,                            // 图片充满容器,自动裁剪成容器大小
          // fit: BoxFit.fitWidth,                      // 宽度裁剪
          // fit: BoxFit.fitHeight,                     // 高度裁剪
          // repeat: ImageRepeat.repeatX,               // X轴平铺
          // repeat: ImageRepeat.repeatY,               // Y轴平铺
          // repeat: ImageRepeat.repeat,                // X和Y轴都平铺
        ),
      ),
    );
  }
}

class MyCircle extends StatelessWidget {                // 实现椭圆图片
  const MyCircle({Key? key}): super(key: key);

  
  Widget build(BuildContext context) {
    return Container(
      height: 288,
      width: 200,
      decoration: BoxDecoration(
        color: Colors.yellow,
        borderRadius: BorderRadius.circular(100),
        image: const DecorationImage(                   // 背景图片
          image: NetworkImage('http://gg.gg/13kuon'),
          fit: BoxFit.cover,
          // fit: BoxFit.fill,                          // 图片会变形
        ),
      ),
    );
  }
}

(2) 实现圆形图片

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('实现圆形图片')),
      body: const ClipImage(),
    ),
  ),);
}

/*
图片地址:http://gg.gg/13jnaa、http://gg.gg/13kuon、http://gg.gg/13kuow
*/

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

  
  Widget build(BuildContext context) {
    return Center(
      child: ClipOval(
        child: Image.network(
          'http://gg.gg/13kuow',
          width: 300,
          height: 300,
          fit: BoxFit.cover,
        ),
      ),
    );
  }
}

(3) 加载本地图片

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('加载本地图片')),
      body: const LocalImage(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.fromLTRB(50, 50, 50, 50),
      height: 500,
      width: 300,
      decoration: const BoxDecoration(
        color: Colors.yellow,
      ),
      child: Image.asset(
        'images/a.jpg',
        // 'images/2.0x/b.jpg',
        // 'images/3.0x/c.jpg',
        // fit: BoxFit.cover,
      ),
    );
  }
}

4-4 图标组件

  • 官方Icons图标(需要梯子才能访问):https://fonts.google.com/icons
  • 自定义图标:阿里巴巴矢量图标库,需要注册账号。
    • 下载自定义图标所对应的.ttf文件,将其复制粘贴到项目根目录的新建文件夹fonts中。
    • 例如在“阿里巴巴矢量图标库”中搜索想要的图标,加入购物车,购物车页点下载代码。
    • 解压下载的压缩包,复制其中的.ttf文件粘贴到fonts中,并修改pubspec.yaml配置文件。
# 例如:myflutter(项目名)目录结构如下
# - fonts/
#   - iconfont.ttf
# - lib/
#   - icon.dart
#   - main.dart
# - pubspec.yaml

# 修改pubspec.yaml配置文件内容,family可依据需求自定义字体,并在lib目录中新建icon.dart文件
fonts:
  - family: Schyler
    fonts:
      - asset: fonts/iconfont.ttf

(1) main.dart

import './icon.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('图标组件')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return Column(
      children: const [
        SizedBox(height: 20),
        Icon(Icons.home, size: 60, color: Colors.red),  // 官方图标
        SizedBox(height: 20),
        Icon(Icons.shop, size: 60, color: Colors.deepPurple),
        SizedBox(height: 20),
        Icon(icon.book, size: 60, color: Colors.lightBlueAccent),
        SizedBox(height: 20),
        Icon(icon.wechat, size: 60, color: Colors.green),
        SizedBox(height: 20),
        Icon(icon.cart, size: 60, color: Colors.cyan),  // 自定义图标
      ],
    );
  }
}

(2) icon.dart

import 'package:flutter/material.dart';

class icon {
  static const IconData book = IconData(
    0X3447,
    // code对应编码,解压的json文件中对应图标的unicode值
    // pubspec.yaml配置文件中,family依据需求自定义的字体
    fontFamily: 'Schyler',
    matchTextDirection: true,
  );
  static const IconData wechat = IconData(
      0Xe637,
      fontFamily: 'Schyler',
      matchTextDirection: true,
  );
  static const IconData cart = IconData(
      0Xf0179,
      fontFamily: 'Schyler',
      matchTextDirection: true,
  );
}

4-5 列表组件

  • 列表布局是开发中最常用的一种布局,通过ListView来定义列表项,支持垂直和水平方向展示。
  • 通过一个属性就可以控制列表的显示方向,其中列表又分成了垂直列表、水平列表和动态列表。
  • 常用参数
    • padding(内边距)、resolve(组件反向排序)、children(列表元素)。
    • scrollDirection(Axis.vertical垂直列表、Axis.horizontal水平列表)。

(1) 垂直列表

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('垂直列表')),
        body: const MyApp(),
      ),
    ),
  );
}

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

  
  Widget build(BuildContext context) {
    return ListView(                                    // 可上下滑动页面进行查看
      children: const <Widget>[
        ListTile(
          leading: Icon(Icons.home, color: Colors.lightBlue),
          title: Text('首页'),
        ),
        Divider(),                                      // 水平分隔线
        ListTile(
          leading: Icon(Icons.assignment, color: Colors.green),
          title: Text('订单'),
          trailing: Icon(Icons.chevron_right_sharp),    // 右侧显示进入页面的“>”图标
        ),
        Divider(),
        ListTile(
          leading: Icon(Icons.payment, color: Colors.orange),
          title: Text('待付'),
          trailing: Icon(Icons.chevron_right_sharp),
        ),
        Divider(),
        ListTile(
          leading: Icon(Icons.favorite, color: Colors.red),
          title: Text('收藏'),
          trailing: Icon(Icons.chevron_right_sharp),
        ),
        Divider(),
        ListTile(
          leading: Icon(Icons.people, color: Colors.blueGrey),
          title: Text('客服'),
          trailing: Icon(Icons.chevron_right_sharp),
        ),
        Divider(),
      ],
    );
  }
}

✧ 图文列表

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('图文列表')),
        body: const MyApp(),
      ),
    ),
  );
}

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

  
  Widget build(BuildContext context) {
    return ListView(
      padding: const EdgeInsets.all(10),
      children: <Widget>[
        ListTile(
          leading: Image.network('http://www.m58.link/wiIfq'),
          title: const Text('博美'),
          subtitle: const Text('风度翩翩,待人友好,不挑衅,外貌也特别有魅力。'),
        ),
        const Divider(),
        ListTile(
          title: const Text('雪纳瑞'),
          subtitle: const Text('个性勇敢,不掉毛,聪明独立更适合办公室驯养。'),
          trailing: Image.network('http://www.m58.link/ahkrp'),
        ),
        const Divider(),
        ListTile(
          leading: Image.network('http://www.m58.link/tnhix'),
          title: const Text('柴犬'),
          subtitle: const Text('聪明,对主人看护,身体干净。'),
        ),
        const Divider(),
        ListTile(
          title: const Text('拉布拉多'),
          subtitle: const Text('性格比较温和,对主人忠心耿耿,外貌憨厚,比较容易驯养。'),
          trailing: Image.network('http://www.m58.link/tqVLU'),
        ),
        const Divider(),
        ListTile(
          leading: Image.network('http://www.m58.link/trjsD'),
          title: const Text('德牧'),
          subtitle: const Text('IQ高,较好训练,忠诚,善于整理头发。'),
        ),
        const Divider(),
        ListTile(
          title: const Text('金毛'),
          subtitle: const Text('大暖男,待人友好,性情温和;快速接受指令,适合于工作犬。'),
          trailing: Image.network('http://www.m58.link/WsKFq'),
        ),
        const Divider(),
        ListTile(
          leading: Image.network('http://www.m58.link/MnMuc'),
          title: const Text('哈士奇'),
          subtitle: const Text('长得真漂亮,走路表情包,喂食比较少。'),
        ),
        const Divider(),
      ],
    );
  }
}

✧ 图片展示

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('图片展示')),
        body: const MyApp(),
      ),
    ),
  );
}

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

  
  Widget build(BuildContext context) {
    return ListView(
      padding: const EdgeInsets.all(10),
      children: <Widget>[
        Image.network('http://www.m58.link/jFcvO'),
        Container(
          padding: const EdgeInsets.fromLTRB(0, 1, 0, 0),
          height: 30,
          child: const Text(
            '大白斑蝶',
            textAlign: TextAlign.center,
            style: TextStyle(fontSize: 20),
          ),
        ),
        Image.network('http://www.m58.link/qmwjR'),
        Container(
          padding: const EdgeInsets.fromLTRB(0, 1, 0, 0),
          height: 30,
          child: const Text(
            '玉带锦蛇蝶',
            textAlign: TextAlign.center,
            style: TextStyle(fontSize: 20),
          ),
        ),
        Image.network('http://www.m58.link/oFHvn'),
        Container(
          padding: const EdgeInsets.fromLTRB(0, 1, 0, 0),
          height: 30,
          child: const Text(
            '蓝凤蝶',
            textAlign: TextAlign.center,
            style: TextStyle(fontSize: 20),
          ),
        ),
      ],
    );
  }
}

(2) 水平列表

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('水平列表')),
        body: const MyApp(),
      ),
    ),
  );
}

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

  
  Widget build(BuildContext context) {
    return SizedBox(
      height: 150,                                      // 与ListView同层可自定义列表高度
      child: ListView(
        scrollDirection: Axis.horizontal,               // 水平列表
        padding: const EdgeInsets.all(10),
        children: <Widget>[
          Container(
            height: 150,                                // 加horizontal则高度自适应
            width: 150,                                 // 不加则宽度自适应,设置无效
            decoration: const BoxDecoration(color: Colors.lightBlue),
            child: Column(
              children: [
                SizedBox(
                  height: 110,
                  child: Image.network('http://www.m58.link/uHlvA', fit: BoxFit.cover),
                ),
                const Text('上海'),
              ],
            ),
          ),
          Container(
            width: 150,
            decoration: const BoxDecoration(color: Colors.lightBlue),
            child: Column(
              children: [
                SizedBox(
                  height: 110,
                  child: Image.network('http://www.m58.link/ifTZY', fit: BoxFit.cover),
                ),
                const Text('广州'),
              ],
            ),
          ),
          Container(
            width: 150,
            decoration: const BoxDecoration(color: Colors.lightBlue),
            child: Column(
              children: [
                SizedBox(
                  height: 110,
                  child: Image.network('http://www.m58.link/NcAie', fit: BoxFit.cover),
                ),
                const Text('北京'),
              ],
            ),
          ),
          Container(
            width: 150,
            decoration: const BoxDecoration(color: Colors.lightBlue),
            child: Column(
              children: [
                SizedBox(
                  height: 110,
                  child: Image.network('http://www.m58.link/rlDUS', fit: BoxFit.cover),
                ),
                const Text('重庆'),
              ],
            ),
          ),
          Container(
            width: 150,
            decoration: const BoxDecoration(color: Colors.lightBlue),
            child: Column(
              children: [
                SizedBox(
                  height: 110,
                  child: Image.network('http://www.m58.link/dtQwt', fit: BoxFit.cover),
                ),
                const Text('南京'),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

(3) 动态列表

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('动态列表')),
        body: const MyApp(),
      ),
    ),
  );
}

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

  List<Widget> _initListData() {                        // 自定义方法
    List<Widget> list=[];
    for(var i=0; i<=7; i++) {
      list.add(ListTile(title: Text('自定义方法---$i')));
    }
    return list;
  }

  
  Widget build(BuildContext context) {
    return ListView(
      children: _initListData(),                        // 调用自定义方法
    );
  }
}

✧ mydata.dart

// 例如:myflutter(项目名)目录结构如下
// - lib/
//   - res/
//     - mydata.dart
// - main.dart
// - icon.dart

List listData=[
  {
    "title": 'Fly Word',
    "author": 'Isla',
    "imageUrl": 'http://www.m58.link/GxYmQ',
  },
  {
    "title": 'Ten Clock',
    "author": 'Olivia',
    "imageUrl": 'http://www.m58.link/TrLBS',
  },
  {
    "title": 'Candy Shop',
    "author": 'Daniel',
    "imageUrl": 'http://www.m58.link/ZqsJH',
  },
  {
    "title": 'Roman Holiday',
    "author": 'Benjamin',
    "imageUrl": 'http://www.m58.link/wTiWA',
  },
  {
    "title": 'Late Autumn Bread',
    "author": 'Samuel',
    "imageUrl": 'http://www.m58.link/erxaA',
  },
];

✧ for动态循环

import './res/mydata.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('for动态循环')),
      body: MyApp(),
    ),
  ),);
}

class MyApp extends StatelessWidget {
  MyApp({Key? key}): super(key: key) {
    print(listData);
  }

  List<Widget> _initListData() {
    List<Widget> tempList=[];
    for(var i=0; i<listData.length; i++) {
      tempList.add(
        ListTile(
          leading: Image.network('${listData[i]["imageUrl"]}'),
          title: Text('${listData[i]["title"]}'),
          subtitle: Text('${listData[i]["author"]}'),
        ),
      );
    }
    return tempList;
  }

  
  Widget build(BuildContext context) {
    return ListView(
      children: _initListData(),
    );
  }
}

✧ map动态循环

import './res/mydata.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('map动态循环')),
      body: MyApp(),
    ),
  ),);
}

class MyApp extends StatelessWidget {
  MyApp({Key? key}): super(key: key) {
    print(listData);
  }

  List<Widget> _initListData() {
    var tempList=listData.map((value) {
      return ListTile(
        leading: Image.network('${value["imageUrl"]}'),
        title: Text('${value["title"]}'),
        subtitle: Text('${value["author"]}'),
      );
    });
    return tempList.toList();
  }

  
  Widget build(BuildContext context) {
    return ListView(
      children: _initListData(),
    );
  }
}

✧ builder动态循环

import './res/mydata.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('builder动态循环')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return ListView.builder(                            // 构造函数
      itemCount: listData.length,
      itemBuilder: (context, i) {
        return ListTile(
          leading: Image.network(listData[i]['imageUrl']),
          title: Text(listData[i]['title']),
          subtitle: Text(listData[i]['author']),
        );
      },
    );
  }
}

4-6 网格组件

  • GridView主要的三种方式:GridView.count、GridView.extent、GridView.builder。
  • 常用属性
    • scollDirection(滚动方法)、padding(内边距)、resolve(组件反向排序)。
    • crossAxisCount(一行的组件数量)、maxCrossAxisExtent(横轴子元素的最大长度)。
    • crossAxisSpacing(水平子组件之间间距)、mainAxisSpacing(垂直子组件之间间距)。
    • childAspectRatio(子组件宽高比例)、gridDelegate(控制布局主要用在GridView.builder里)。

(1) count实现

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('count实现')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return GridView.count(
      crossAxisCount: 5,                                // 一行显示5个图标
      children: const [
        Icon(Icons.pedal_bike),
        Icon(Icons.home),
        Icon(Icons.ac_unit),
        Icon(Icons.search),
        Icon(Icons.settings),
        Icon(Icons.airport_shuttle),
        Icon(Icons.all_inclusive),
        Icon(Icons.beach_access),
        Icon(Icons.cake),
        Icon(Icons.circle),
      ],
    );
  }
}

(2) extent实现

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('extent实现')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return GridView.extent(
      maxCrossAxisExtent: 100,                          // 横轴子元素的最大长度
      children: const [
        Icon(Icons.pedal_bike),
        Icon(Icons.home),
        Icon(Icons.ac_unit),
        Icon(Icons.search),
        Icon(Icons.settings),
        Icon(Icons.airport_shuttle),
        Icon(Icons.all_inclusive),
        Icon(Icons.beach_access),
        Icon(Icons.cake),
        Icon(Icons.circle),
      ],
    );
  }
}

(3) 常用属性实例

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('常用属性实例')),
      body: const MyApp(),
    ),
  ),);
}

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

  List<Widget> _initGridViewData() {
    List<Widget> tempList=[];
    for(var i=1; i<16; i++) {
      tempList.add(
        Container(
          alignment: Alignment.center,
          decoration: const BoxDecoration(
            color: Colors.blue,
          ),
          child: Text("第${i}个元素",
            style: const TextStyle(fontSize: 20),
          ),
        ),
      );
    }
    return tempList;
  }

  
  Widget build(BuildContext context) {
    return GridView.count(
      padding: const EdgeInsets.all(2),                 // 四周间距
      crossAxisSpacing: 2,                              // 配置水平子组件之间的间距
      mainAxisSpacing: 2,                               // 配置垂直子组件之间的间距
      crossAxisCount: 5,                                // 一行显示5个图标,count实现
      // maxCrossAxisExtent: 100,                       // 与extent实现属性相同
      childAspectRatio: 0.6,                            // 宽高比
      children: _initGridViewData(),
    );
  }
}

(4) count动态列表

import '../res/mydata.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('count动态列表')),
      body: const MyApp(),
    ),
  ),);
}

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

  List<Widget> _initGridViewData() {
    var tempList=listData.map((value) {
      return Container(
        decoration: BoxDecoration(
          border: Border.all(color: Colors.black26),
        ),
        child: Column(
          children: [
            Image.network(value["imageUrl"]),
            const SizedBox(height: 10),
            Text(
              value["title"],
              style: const TextStyle(fontSize: 13),
            ),
          ],
        ),
      );
    });
    return tempList.toList();
  }

  
  Widget build(BuildContext context) {
    return GridView.count(
      padding: const EdgeInsets.all(2),                 // 四周间距
      crossAxisSpacing: 2,                              // 配置水平子组件之间的间距
      mainAxisSpacing: 2,                               // 配置垂直子组件之间的间距
      crossAxisCount: 3,                                // 一行显示3个图标,count实现
      // maxCrossAxisExtent: 100,                       // 与extent实现属性相同
      childAspectRatio: 0.8,                            // 宽高比
      children: _initGridViewData(),
    );
  }
}

(5) builder动态列表

import '../res/mydata.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('builder动态列表')),
      body: const MyApp(),
    ),
  ),);
}

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

  Widget _initGridViewData(context, index) {
    return Container(
      decoration: BoxDecoration(
        border: Border.all(color: Colors.black26),
      ),
      child: Column(
        children: [
          Image.network(listData[index]["imageUrl"]),
          const SizedBox(height: 10),
          Text(
            listData[index]["title"],
            style: const TextStyle(fontSize: 13),
          ),
        ],
      ),
    );
  }

  
  Widget build(BuildContext context) {
    return GridView.builder(
      padding: const EdgeInsets.all(2),                 // 四周间距
      itemCount: listData.length,                       // 传入参数长度

      // crossAxisSpacing: 2,                           // 配置水平子组件之间的间距
      // mainAxisSpacing: 2,                            // 配置垂直子组件之间的间距
      // crossAxisCount: 3,                             // 一行显示3个图标
      // childAspectRatio: 0.8,                         // 宽高比

      // gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
      //   crossAxisSpacing: 2,
      //   mainAxisSpacing: 2,
      //   crossAxisCount: 3,
      //   childAspectRatio: 0.8,
      // ),                                             // 类似于count与extent实现的区别

      gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
        crossAxisSpacing: 2,
        mainAxisSpacing: 2,
        maxCrossAxisExtent: 150,
        childAspectRatio: 0.8,
      ),

      itemBuilder: _initGridViewData,
    );
  }
}

5 页面布局

  • StatelessWidget和StatefulWidget
    • 一个Widgets可被定义为无状态组件(StatelessWidget)和有状态组件(StatefulWidget)。
    • StatelessWidget是一个不需要可变状态的小组件类型,只需要初始化时的构造函数。
    • StatefulWidget是个持有可变状态的小组件类型,复写createState()返回State对象。
  • Row和Column:常用的布局类小组件,Row使用水平方向排列子组件,Column则垂直排列。
  • Expanded
    • Expanded小组件是父组件所在主轴方向上的包裹组件,用于扩展子组件填充剩余空间。
    • 当Expanded小组件嵌套在Row,或Column小组件中时,会根据自身比例分配可用空间。
  • Stack和Positioned
    • Stack组件用于将多个子组件叠放在一起,Positioned组件用于获取并约束子组件的位置和大小。
    • 而使用Align组件和PositionedDirectional组件,则可以控制小组件在相对定位坐标系中的位置。
  • SingleChildScrollView
    • 产生可滚动的组件区域,只有一个子组件,当使用Column或Row嵌套大量组件时可能超出屏幕尺寸。
    • 此时用SingleChildScrollView组件将其包裹,使之在应用程序中可滚动并能够适应不同的屏幕尺寸。

5-1 填充组件

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('填充组件')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    // return Container(
    //   padding: const EdgeInsets.all(160),
    //   child: const Text("Hello, Flutter!"),
    // );
    return const Padding(                               // 占用内存比Container小
      padding: EdgeInsets.all(160),                     // Padding组件属性比较单一
      child: Text("Hello, Flutter!"),
    );
  }
}

5-2 线性布局

  • mainAxisAlignment(主轴的排序方式)、crossAxisAlignment(次轴的排序方式)、children(组件子元素)。

(1) 水平布局组件

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('水平布局组件')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      height: double.infinity,                          // 无穷大,占满屏幕
      color: Colors.black12,
      child: Row(                                       // 外部若没有Container,行将自适应
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        crossAxisAlignment: CrossAxisAlignment.center,  // 相对于Container设置的
        children: [
          IconContainer(Icons.home),
          IconContainer(Icons.search, color: Colors.yellow),
          IconContainer(Icons.ac_unit_sharp, color: Colors.orange),
        ],
      ),
    );
    // return Padding(
    //   padding: const EdgeInsets.all(10),
    //   child: Row(                                    // 水平布局
    //     mainAxisAlignment: MainAxisAlignment.spaceBetween,
    //     children: [
    //       IconContainer(Icons.home),
    //       // IconContainer(Icons.search, color: Colors.yellow),
    //       IconContainer(Icons.ac_unit_sharp, color: Colors.orange),
    //     ],
    //   ),
    // );
  }
}

class IconContainer extends StatelessWidget {
  Color color;                                          // 默认传入颜色为红色
  IconData icon;                                        // 没有默认颜色使用required关键字
  IconContainer(this.icon, {Key? key, this.color=Colors.red}): super(key: key);

  
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      height: 130,
      width: 130,
      color: color,
      child: Icon(icon, color: Colors.white, size: 28),
    );
  }
}

(2) 垂直布局组件

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('垂直布局组件')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      height: double.infinity,                          // 无穷大,占满屏幕
      color: Colors.black12,
      child: Column(                                    // 垂直布局
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        crossAxisAlignment: CrossAxisAlignment.center,  // 相对于Container设置的
        children: [
          IconContainer(Icons.home),
          IconContainer(Icons.search, color: Colors.yellow),
          IconContainer(Icons.ac_unit_sharp, color: Colors.orange),
        ],
      ),
    );
    // return Padding(
    //   padding: const EdgeInsets.all(10),
    //   child: Column(
    //     mainAxisAlignment: MainAxisAlignment.spaceBetween,
    //     children: [
    //       IconContainer(Icons.home),
    //       IconContainer(Icons.search, color: Colors.yellow),
    //       IconContainer(Icons.ac_unit_sharp, color: Colors.orange),
    //     ],
    //   ),
    // );
  }
}

class IconContainer extends StatelessWidget {
  Color color;                                          // 默认传入颜色为红色
  IconData icon;                                        // 没有默认颜色使用required关键字
  IconContainer(this.icon, {Key? key, this.color=Colors.red}): super(key: key);

  
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      height: 130,
      width: 130,
      color: color,
      child: Icon(icon, color: Colors.white, size: 28),
    );
  }
}

5-3 弹性布局

  • Flex组件可以沿着水平或垂直方向排列子组件,如果知道主轴方向,使用Row或Column会很方便。
  • Row和Column都继承自Flex,参数基本相同,所以能使用Flex的地方基本都可使用Row和Column。
  • Flex组件功能强大,也可以和Expanded组件配合实现弹性布局。

(1) 水平左右比例

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('水平左右比例')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return Flex(
      direction: Axis.horizontal,                       // 水平位置
      // direction: Axis.vertical,                      // 垂直位置
      children: [
        Expanded(
          child: IconContainer(Icons.home),             // 该元素设置宽度无效
          flex: 1,                                      // 左右侧比例1:2
        ),
        Expanded(
          child: IconContainer(Icons.ac_unit_sharp, color: Colors.orange),
          flex: 2,
        ),
      ],
    );
  }
}

class IconContainer extends StatelessWidget {
  Color color;                                          // 默认传入颜色为红色
  IconData icon;                                        // 没有默认颜色使用required关键字
  IconContainer(this.icon, {Key? key, this.color=Colors.red}): super(key: key);

  
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      height: 130,
      width: 130,
      color: color,
      child: Icon(icon, color: Colors.white, size: 28),
    );
  }
}

(2) 水平右侧固定

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('水平右侧固定')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return Row(
      // direction: Axis.horizontal,                    // 水平位置
      // direction: Axis.vertical,                      // 垂直位置
      children: [
        Expanded(
          child: IconContainer(Icons.home),             // 该元素设置宽度无效
          flex: 1,
        ),
        IconContainer(Icons.ac_unit_sharp, color: Colors.orange),
      ],
    );
  }
}

class IconContainer extends StatelessWidget {
  Color color;                                          // 默认传入颜色为红色
  IconData icon;                                        // 没有默认颜色使用required关键字
  IconContainer(this.icon, {Key? key, this.color=Colors.red}): super(key: key);

  
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      height: 80,                                       // 右侧固定宽度,左侧自适应
      width: 80,
      color: color,
      child: Icon(icon, color: Colors.white, size: 28),
    );
  }
}

(3) 实现弹性布局

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('实现弹性布局')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return ListView(
      children: [
        Container(
          width: double.infinity,
          height: 200,
          color: Colors.black,
        ),
        Row(
          children: [
            Expanded(
              child: SizedBox(
                height: 200,                            // 水平方向考虑高度
                child: Image.network('http://www.m58.link/pOQYO', fit: BoxFit.cover),
              ),
              flex: 2,
            ),
            Expanded(
              flex: 1,
              child: SizedBox(
                height: 200,                            // 垂直方向则考虑宽度
                child: Column(
                  children: [
                    Expanded(                           // 图片可能不会铺满,使用SizedBox
                      child: Image.network(
                        'http://www.m58.link/BiPCY',
                        fit: BoxFit.cover,
                      ),
                      flex: 1,
                    ),
                    const SizedBox(height: 1),
                    Expanded(
                      child: SizedBox(
                        width: double.infinity,         // 占满屏幕
                        child: Image.network(
                          'http://www.m58.link/dDobp',
                          fit: BoxFit.cover,
                        ),
                      ),
                      flex: 1,
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ],
    );
  }
}

5-4 层叠组件

  • Stack即堆
    • 使用Stack,或Stack结合Align,或Stack结合Positioned实现页面的定位布局。
    • Stack属性包括:alignment(配置所有子元素的显示位置)、children(子组件)。
import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('层叠组件')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return Stack(                                       // Column则排列一起
      alignment: Alignment.center,
      children: [
        Container(
          height: 400,
          width: 400,
          color: Colors.red,
        ),
        Container(
          height: 200,
          width: 200,
          color: Colors.yellow,
        ),
        const Text("Hello, Flutter!"),
      ],
    );
  }
}

(1) Positioned

  • Stack组件结合Positioned组件可以控制每个子元素的显示位置。
  • top(子元素距离顶部的距离)、bottom(子元素距离底部的距离)、child(子组件)。
  • left(子元素距离左侧距离)、right(子元素距离右侧距离)、height(子组件高度)。
  • width(子组件的宽度,注意宽度和高度必须是固定值,没法使用double.infinity)。
import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('Positioned')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return Container(
      height: 400,
      width: 400,
      color: Colors.red,
      child: Stack(                                     // Stack是相对于外部容器进行定位
        children: [                                     // 若无外部容器就相对于整个屏幕定位
          Positioned(
            left: 0,                                    // 居于容器左下角
            bottom: 0,
            child: Container(
              height: 200,
              width: 200,
              color: Colors.yellow,
            ),
          ),
          const Positioned(
            right: 0,                                   // 居于容器右侧中间
            top: 200,
            child: Text("Hello, Flutter!"),
          ),
        ],
      ),
    );
  }
}

(2) 浮动导航功能

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('浮动导航功能')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;           // 获取设备的宽度和高度
    return Stack(
      children: [
        ListView(
          padding: const EdgeInsets.only(top: 50),      // 将隐藏的列表01显示出来
          children: const [
            ListTile(title: Text("列表01")),
            ListTile(title: Text("列表02")),
            ListTile(title: Text("列表03")),
            ListTile(title: Text("列表04")),
            ListTile(title: Text("列表05")),
            ListTile(title: Text("列表06")),
            ListTile(title: Text("列表07")),
            ListTile(title: Text("列表08")),
            ListTile(title: Text("列表09")),
            ListTile(title: Text("列表10")),
            ListTile(title: Text("列表11")),
            ListTile(title: Text("列表12")),
            ListTile(title: Text("列表13")),
            ListTile(title: Text("列表14")),
            ListTile(title: Text("列表15")),
            ListTile(title: Text("列表16")),
            ListTile(title: Text("列表17")),
            ListTile(title: Text("列表18")),
            ListTile(title: Text("列表19")),
            ListTile(title: Text("列表20")),
          ],
        ),
        Positioned(
          left: 0,
          top: 0,
          width: size.width,                            // 无法使用double.infinity
          height: 50,
          child: Row(
            children: [
              Expanded(flex: 1, child: Container(
                alignment: Alignment.center,
                height: 50,
                color: Colors.purple,
                child: const Text(
                  "二级导航", 
                  style: TextStyle(color: Colors.white),
                ),
              )),
            ],
          ),
        ),
      ],
    );
  }
}

(3) 优化浮动导航

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('优化浮动导航')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;           // 获取设备的宽度和高度
    return Stack(
      children: [
        ListView(
          padding: const EdgeInsets.only(bottom: 50),
          children: const [
            ListTile(title: Text("列表01")),
            ListTile(title: Text("列表02")),
            ListTile(title: Text("列表03")),
            ListTile(title: Text("列表04")),
            ListTile(title: Text("列表05")),
            ListTile(title: Text("列表06")),
            ListTile(title: Text("列表07")),
            ListTile(title: Text("列表08")),
            ListTile(title: Text("列表09")),
            ListTile(title: Text("列表10")),
            ListTile(title: Text("列表11")),
            ListTile(title: Text("列表12")),
            ListTile(title: Text("列表13")),
            ListTile(title: Text("列表14")),
            ListTile(title: Text("列表15")),
            ListTile(title: Text("列表16")),
            ListTile(title: Text("列表17")),
            ListTile(title: Text("列表18")),
            ListTile(title: Text("列表19")),
            ListTile(title: Text("列表20")),
          ],
        ),
        Positioned(
          left: 0,
          bottom: 0,                                    // 二级导航居于底部
          child: Container(
            width: size.width,                          // 配置子元素的宽度和高度
            height: 50,
            alignment: Alignment.center,
            color: Colors.purple,
            child: const Text(
              "二级导航",
              style: TextStyle(color: Colors.white),
            ),
          ),
        ),
      ],
    );
  }
}

(4) Align方法介绍

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('Align方法介绍')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return Container(
      // alignment: Alignment.topCenter,                // 方法一
      width: 200,
      height: 200,
      color: Colors.red,

      // child: const Align(                            // 方法二
      //   alignment: Alignment.center,
      //   child: Text("Hello, Flutter!"),
      // ),

      // child: const Center(                           // 方法三
      //   child: Text("Hello, Flutter!"),
      // ),

      // (Alignment.x*childwidth/2+childwidth/2,Alignment.y*childHeight/2+childHeight/2)
      child: const Align(
        alignment: Alignment(-1, 1),                    // 通过坐标转换公式计算偏移位置
        child: Text("Hello, Flutter!"),
      ),
    );
  }
}

(5) Stack结合Align

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('Stack结合Align')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    // return Stack(
    //   children: const [
    //     Align(alignment: Alignment.topLeft, child: Text("收藏")),
    //     Align(alignment: Alignment.topRight, child: Text("购买")),
    //   ],
    // );
    return Column(
      children: [
        SizedBox(
          width: double.infinity,
          height: 40,
          child: Stack(
            children: const [
              Align(alignment: Alignment.topLeft, child: Text("收藏")),
              Align(alignment: Alignment.topRight, child: Text("购买")),
            ],
          ),
        ),
        SizedBox(
          width: double.infinity,
          height: 40,
          child: Stack(                                 // 相对于容器SizedBox来定位
            children: const [
              Positioned(left: 10, child: Text("收藏")),
              Positioned(right: 10, child: Text("购买")),
            ],
          ),
        ),
      ],
    );
  }
}

5-5 比例组件

  • 作用是根据设置调整子元素child的宽高比,首先在布局限制条件允许的范围内尽可能的扩展。
  • Widget的高度由宽度和比率决定,类似于BoxFit中的contain,按照固定比率去尽量占满区域。
  • 如果满足所有限制条件后仍无法找到一个可行尺寸,将优先适应布局限制条件,忽略设置的比率。
import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('比例组件')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    // 页面上显示一个容器,宽度是屏幕的宽度,高度是宽度的一半
    return AspectRatio(
      aspectRatio: 2/1,
      child: Container(
        color: Colors.red,
      ),
    );
  }
}

5-6 卡片组件

  • Card是卡片组件块,内容由大多数类型组件构成,具有圆角和阴影,看起来更具立体感。
  • margin(外边距)、elevation(阴影值的深度)、color(背景颜色)、shadowColor(阴影颜色)。
  • child(子组件)、Shape(Card的阴影效果,默认为圆角长方形边)、clipBehavior(内容溢出的剪切方式)。
    • Clip.none(不剪切)、Clip.harEdge(剪切但不抗锯齿)、Clip.antiAlias(剪切并抗锯齿)。
    • Clip.antiAliasWithSaveLayer:带有抗锯齿的剪切,并在剪切之后立即保存saveLayer。
  • AspectRatio(宽高比,最终可能不会根据这个值布局,具体看综合因素,只是一个参考值)。
import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('卡片组件')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return ListView(
      children: [
        Card(
          shape: RoundedRectangleBorder(                // 卡片的阴影效果
            borderRadius: BorderRadius.circular(30),
          ),
          elevation: 10,                                // 卡片阴影值的深度
          margin: const EdgeInsets.all(30),             // 边距
          child: Column(
            children: const [
              ListTile(
                title: Text("Luxury", style: TextStyle(fontSize: 28)),
                subtitle: Text("高级软件工程师"),
              ),
              Divider(),                                // 分割线
              ListTile(title: Text("电话:18856666110")),
              ListTile(title: Text("地址:福建省厦门市思明区")),
            ],
          ),
        ),
        // const SizedBox(height: 10),                  // 两卡片之间的距离
        Card(
          elevation: 10,                                // 卡片阴影
          color: Colors.greenAccent,                    // 背景颜色
          child: Column(
            children: const [
              ListTile(
                title: Text("Aurora", style: TextStyle(fontSize: 28)),
                subtitle: Text("高级开发工程师"),
              ),
              Divider(),                                // 分割线
              ListTile(title: Text("电话:18856666120")),
              ListTile(title: Text("地址:福建省厦门市湖里区")),
            ],
          ),
        ),
      ],
    );
  }
}

(1) 圆形图片

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('圆形图片')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return ListView(
      children: [
        Card(
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(10),
          ),
          elevation: 20,
          margin: const EdgeInsets.all(10),
          child: Column(
            children: [
              AspectRatio(
                aspectRatio: 16/9,
                child: Image.network("http://www.m58.link/yWnmo", fit: BoxFit.cover),
              ),
              ListTile(
                leading: ClipOval(                      // ClipOval实现圆形图片
                  child: Image.network(
                    "http://www.m58.link/yWnmo",
                    fit: BoxFit.cover,
                    height: 50,
                    width: 50,
                  ),
                ),
                title: const Text("桃夭"),
                subtitle: const Text("桃之夭夭,灼灼其华。"),
              ),
            ],
          ),
        ),
        Card(
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(10),
          ),
          elevation: 20,
          margin: const EdgeInsets.all(10),
          child: Column(
            children: [
              AspectRatio(
                aspectRatio: 16/9,
                child: Image.network("http://www.m58.link/abMGM", fit: BoxFit.cover),
              ),
              const ListTile(
                leading: CircleAvatar(                  // CircleAvatar实现圆形图片
                  radius: 25,                           // 设置头像半径
                  backgroundImage: NetworkImage("http://www.m58.link/abMGM"),
                ),
                title: Text("蒹葭"),
                subtitle: Text("蒹葭苍苍,白露为霜。"),
              ),
            ],
          ),
        ),
      ],
    );
  }
}

(2) 循环遍历

import './res/mydata.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('循环遍历')),
      body: const MyApp(),
    ),
  ),);
}

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

  List<Widget> _initCardData() {
    var tempList = listData.map((value) {
      return Card(
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(10),
        ),
        elevation: 20,
        margin: const EdgeInsets.all(10),
        child: Column(
          children: [
            AspectRatio(
              aspectRatio: 16/9,
              child: Image.network(value['imageUrl'], fit: BoxFit.cover),
            ),
            ListTile(
              leading: ClipOval(                        // ClipOval实现圆形图片
                child: Image.network(
                  value['imageUrl'],
                  fit: BoxFit.cover,
                  height: 50,
                  width: 50,
                ),
              ),
              title: Text(value['title']),
              subtitle: Text(value['author']),
            ),
          ],
        ),
      );
    });
    return tempList.toList();
  }

  
  Widget build(BuildContext context) {
    return ListView(
      children: _initCardData(),                        // 调用方法
    );
  }
}

5-7 按钮组件

  • onPressed(必填参,按下按钮时触发回调,接收一个方法,传null表禁用,显示相关样式)。
  • child(子组件)、style(通过ButtonStyle进行装饰,ButtonStyle里面的常用参数,如下所示)。
    • ElevatedButton:凸起按钮,默认带有阴影和灰色背景,按下后阴影变大。
    • TextButton:文本按钮,默认背景透明并且不带阴影,按下后会有背景色。
    • OutlinedButton:默认带边框,不带阴影且背景透明,按下后边框颜色变亮同时出现背景和阴影。
    • IconButton:是一个可点击的Icon图标,不包含文字,默认是没有背景的,点击后将会出现背景。
    • 带图标的按钮:ElevatedButton、TextButton、OutlinedButton都有一个icon构造函数,可带图标。
import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('按钮组件')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return ListView(
      children: [
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            ElevatedButton(                             // 凸起按钮
              onPressed: (){print("ElevatedButton");},  // 打印ElevatedButton
              child: const Text("普通按钮"),
            ),
            TextButton(                                 // 文本按钮
              onPressed: (){},
              child: const Text("文本按钮"),
            ),
            const OutlinedButton(                       // 带边框的按钮
              onPressed: null,
              child: Text("边框按钮"),
            ),
            IconButton(onPressed: (){}, icon: const Icon(Icons.thumb_up)),
          ],
        ),
        const SizedBox(height: 20),
        Row(                                            // 调整按钮位置
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            ElevatedButton.icon(                        // 带图标的按钮
              onPressed: (){},
              icon: const Icon(Icons.send),
              label: const Text("发送"),
            ),
            TextButton.icon(
              onPressed: (){},
              icon: const Icon(Icons.info),
              label: const Text("消息"),
            ),
            OutlinedButton.icon(
              onPressed: (){},
              icon: const Icon(Icons.add),
              label: const Text("增加"),
            ),
          ],
        ),
        const SizedBox(height: 20),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            ElevatedButton(                             // 修改按钮和图标的颜色
              style: ButtonStyle(                       // 背景颜色和文字颜色
                backgroundColor: MaterialStateProperty.all(Colors.red),
                foregroundColor: MaterialStateProperty.all(Colors.white),
              ),
              onPressed: (){print("ElevatedButton");},
              child: const Text("普通按钮"),
            ),
          ],
        ),
      ],
    );
  }
}

(1) 修改宽和高

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('修改宽和高')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return ListView(
      children: [
        const SizedBox(height: 20),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            Container(
              height: 60,
              width: 180,
              child: ElevatedButton(
                onPressed: (){},
                child: const Text("确认"),
              ),
            ),
            SizedBox(
              height: 60,
              width: 180,
              child: ElevatedButton.icon(
                onPressed: (){},
                icon: const Icon(Icons.search),
                label: const Text("搜索"),
              ),
            ),
          ],
        ),
      ],
    );
  }
}

(2) 自适应按钮

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('自适应按钮')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return ListView(
      children: [
        const SizedBox(height: 10),
        Row(
          // mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            Expanded(
              flex: 1,                                  // 占满屏幕
              child: Container(
                margin: const EdgeInsets.all(20),
                height: 40,
                // width: 180,                          // 没效果了
                child: ElevatedButton(
                  onPressed: (){},
                  style: ButtonStyle(
                    backgroundColor: MaterialStateProperty.all(
                      const Color.fromARGB(220, 219, 35, 35),
                    ),
                    foregroundColor: MaterialStateProperty.all(Colors.white)
                  ),
                  child: const Text("登录"),
                ),
              ),
            ),
          ],
        ),
      ],
    );
  }
}

(3) 圆形与圆角

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('圆形与圆角')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return ListView(
      children: [
        const SizedBox(height: 10),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            SizedBox(
              height: 60,
              width: 60,
              child: ElevatedButton(
                style: ButtonStyle(
                  shape: MaterialStateProperty.all(
                    const CircleBorder(
                      side: BorderSide(
                        width: 3,
                        color: Colors.yellow,
                      ),
                    ),
                  ),
                ),
                onPressed: (){},
                child: const Text("圆形"),
              ),
            ),
            ElevatedButton(
              style: ButtonStyle(
                shape: MaterialStateProperty.all(
                  RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(12),
                  ),
                ),
              ),
              onPressed: (){},
              child: const Text("圆角"),
            ),
          ],
        ),
      ],
    );
  }
}

(4) 边框的修改

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('边框的修改')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return ListView(
      children: [
        const SizedBox(height: 10),
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            OutlinedButton(
              style: ButtonStyle(
                side: MaterialStateProperty.all(
                  const BorderSide(width: 1, color: Colors.red),
                ),
              ),
              onPressed: (){},
              child: const Text("带边框的按钮"),
            ),
          ],
        ),
      ],
    );
  }
}

5-8 Wrap组件

  • 该组件可以实现流布局,单行的Wrap和Row表现几乎一致,单列的Wrap和Column也表现几乎一致。
  • Row与Column都是单行单列,Wrap则突破限制,mainAxis上空间不足时,向crossAxis上扩展显示。
  • 属性说明
    • direction(主轴的方向,默认水平)、spacing(主轴方向上的间距)。
    • alignment(主轴的对齐方式)、textDirection(文本方向)、runSpacing(run的间距)。
    • verticalDirection(定义了children的摆放顺序,默认是down,详见Flex相关属性)。
    • runAlignment(run的对齐方式,run可理解为新的行或列,水平方向布局可理解为新的一行)。

(1) Wrap组件使用

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('Wrap组件使用')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    // return Button("第1集", onPressed: (){});
    return Padding(
      padding: const EdgeInsets.all(10),
      child: Wrap(
        // alignment: WrapAlignment.center,             // 居中显示
        spacing: 5,                                     // x轴上的间距数(水平间距)
        runSpacing: 10,                                 // y轴上的间距数(垂直间距)
        // direction: Axis.vertical,                    // 垂直方向排列
        children: [
          Button("第1集", onPressed: (){}),
          Button("第2集", onPressed: (){}),
          Button("第3集", onPressed: (){}),
          Button("第4集", onPressed: (){}),
          Button("第5集", onPressed: (){}),
          Button("第6集", onPressed: (){}),
          Button("第7集", onPressed: (){}),
          Button("第8集", onPressed: (){}),
          Button("第9集", onPressed: (){}),
          Button("第10集", onPressed: (){}),
          Button("第11集", onPressed: (){}),
          Button("第12集", onPressed: (){}),
          Button("第13集", onPressed: (){}),
          Button("第14集", onPressed: (){}),
          Button("第15集", onPressed: (){}),
          Button("第16集", onPressed: (){}),
          Button("第17集", onPressed: (){}),
          Button("第18集", onPressed: (){}),
        ],
      ),
    );
  }
}

class Button extends StatelessWidget {                  // 自定义按钮组件
  String text;                                          // 按钮的文字
  void Function()? onPressed;                           // 方法
  Button(this.text, {Key? key, required this.onPressed}): super(key: key);

  
  Widget build(BuildContext context) {
    return ElevatedButton(
      style: ButtonStyle(
        backgroundColor: MaterialStateProperty.all(
          const Color.fromRGBO(244, 244, 244, 244),
        ),
        foregroundColor: MaterialStateProperty.all(Colors.black45),
      ),
      onPressed: onPressed,
      child: Text(text),
    );
  }
}

(2) 搜索页面布局

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: const Text('搜索页面布局')),
      body: const MyApp(),
    ),
  ),);
}

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

  
  Widget build(BuildContext context) {
    return ListView(
      padding: const EdgeInsets.all(10),
      children: [
        Row(
          children: [
            Text("热搜", style: Theme.of(context).textTheme.titleLarge),
          ],
        ),
        const Divider(),
        Wrap(
          spacing: 10,
          runSpacing: 10,
          children: [
            Button("女装", onPressed: (){}),
            Button("笔记本", onPressed: (){}),
            Button("玩具", onPressed: (){}),
            Button("文学", onPressed: (){}),
            Button("时尚", onPressed: (){}),
            Button("扭扭棒", onPressed: (){}),
            Button("健身", onPressed: (){}),
            Button("台式机", onPressed: (){}),
          ],
        ),
        const SizedBox(height: 10),
        Row(
          children: [
            Text("历史记录", style: Theme.of(context).textTheme.titleLarge),
          ],
        ),
        const Divider(),
        Column(
          children: const [
            ListTile(title: Text("女装")),
            Divider(),
            ListTile(title: Text("手机")),
            Divider(),
            ListTile(title: Text("电脑")),
            Divider(),
          ],
        ),
        const SizedBox(height: 40),
        // OutlinedButton.icon(                         // 自适应
        //   onPressed: (){},
        //   icon: const Icon(Icons.delete),
        //   label: const Text("清空历史记录"),
        // ),
        Padding(
          padding: const EdgeInsets.all(40),
          child: OutlinedButton.icon(
            style: ButtonStyle(
              foregroundColor: MaterialStateProperty.all(Colors.black45),
            ),
            onPressed: (){},
            icon: const Icon(Icons.delete),
            label: const Text("清空历史记录"),
          ),
        ),
      ],
    );
  }
}

class Button extends StatelessWidget {                  // 自定义按钮组件
  String text;                                          // 按钮的文字
  void Function()? onPressed;                           // 方法
  Button(this.text, {Key? key, required this.onPressed}): super(key: key);

  
  Widget build(BuildContext context) {
    return ElevatedButton(
      style: ButtonStyle(
        backgroundColor: MaterialStateProperty.all(
          const Color.fromRGBO(244, 244, 244, 244),
        ),
        foregroundColor: MaterialStateProperty.all(Colors.black45),
      ),
      onPressed: onPressed,
      child: Text(text),
    );
  }
}

文章作者: bsf
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 bsf !
评论
 上一篇
有状态组件(一) 有状态组件(一)
Flutter有状态组件是指可以在生命周期内维护状态的组件,可以通过数据的变化来改变自身的外观和行为。
2023-05-11
下一篇 
Dart基础 Dart基础
Dart诞生于2011年10月10日,是一种“结构化的Web编程”语言,应用于Web、服务器、移动应用等领域的开发。
2023-01-08
  目录