喜迎
春节

页面布局


Flutter页面布局主要使用Row和Column两种基本布局组件,可组合使用并结合Expanded、Flex等组件实现更灵活的页面布局。

1 页面布局

  • 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组件将其包裹,使之在应用程序中可滚动并能够适应不同屏幕尺寸。

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);

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

3 线性布局

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

3-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);

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      // 无穷大,占满屏幕
      height: double.infinity,
      color: Colors.black12,
      // 外部若没有Container,行将自适应
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        // 相对于Container设置的
        crossAxisAlignment: CrossAxisAlignment.center,
        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;
  // 没有默认颜色使用required关键字
  IconData icon;
  IconContainer(this.icon, {Key? key, this.color = Colors.red})
      : super(key: key);

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

3-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);

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      // 无穷大,占满屏幕
      height: double.infinity,
      color: Colors.black12,
      // 垂直布局
      child: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        // 相对于Container设置的
        crossAxisAlignment: CrossAxisAlignment.center,
        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;
  // 没有默认颜色使用required关键字
  IconData icon;
  IconContainer(this.icon, {Key? key, this.color = Colors.red})
      : super(key: key);

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

4 弹性布局

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

4-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);

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

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

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

4-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);

  @override
  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;
  // 没有默认颜色使用required关键字
  IconData icon;
  IconContainer(this.icon, {Key? key, this.color = Colors.red})
      : super(key: key);

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

4-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);

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: [
        Container(
          width: double.infinity,
          height: 200,
          color: Colors.white,
        ),
        Row(
          children: [
            Expanded(
              child: SizedBox(
                // 水平方向考虑高度
                height: 200,
                child: Image.network(
                  'https://gimg2.baidu.com/image_search'
                  '/src=http%3A%2F%2Fc-ssl.duitang.com%'
                  '2Fuploads%2Fblog%2F202009%2F20%2F202'
                  '00920220228_e6d34.jpg&refer=http%3A%'
                  '2F%2Fc-ssl.duitang.com&app=2002&size'
                  '=f9999,10000&q=a80&n=0&g=0n&fmt=auto'
                  '?sec=1725155093&t=b3ea8cb72ee2c8770fda07b2e4bd5872',
                  fit: BoxFit.cover,
                ),
              ),
              flex: 2,
            ),
            Expanded(
              flex: 1,
              child: SizedBox(
                // 垂直方向则考虑宽度
                height: 200,
                child: Column(
                  children: [
                    // 图片可能不会铺满,使用SizedBox
                    Expanded(
                      child: Image.network(
                        'https://c-ssl.dtstatic.com/uploads'
                        '/blog/202211/05/20221105172125_7ca'
                        '2f.thumb.1000_0.jpg',
                        fit: BoxFit.cover,
                      ),
                      flex: 1,
                    ),
                    const SizedBox(height: 1),
                    Expanded(
                      child: SizedBox(
                        // 占满屏幕
                        width: double.infinity,
                        child: Image.network(
                          'https://pic.90sheji.com/original_origin_pic'
                          '/18/10/25/52c0e55eebb42d07e3d23ae7dde692df.'
                          'png%21/fwfh/5808x0/clip/0x3856a0a0/quality/'
                          '90/unsharp/true/compress/true/watermark/url'
                          '/LzkwX3dhdGVyX3Y2LnBuZw==/repeat/true',
                          fit: BoxFit.cover,
                        ),
                      ),
                      flex: 1,
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ],
    );
  }
}

5 层叠组件

  • 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);

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

5-1 Positioned

  • Stack组件结合Positioned组件可以控制每个子元素的显示位置。
  • left(子元素距离左侧距离)、right(子元素距离右侧距离)、height(子组件高度)。
  • top(子元素距离顶部的距离)、bottom(子元素距离底部的距离)、child(子组件)。
  • 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);

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 400,
      width: 400,
      color: Colors.green,
      // Stack是相对于外部容器进行定位
      child: 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!'),
          ),
        ],
      ),
    );
  }
}

5-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);

  @override
  Widget build(BuildContext context) {
    // 获取设备的宽度和高度
    final size = MediaQuery.of(context).size;
    return Stack(
      children: [
        ListView(
          // 将隐藏的列表01显示出来
          padding: const EdgeInsets.only(top: 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,
          top: 0,
          // 无法使用double.infinity
          width: size.width,
          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),
                  ),
                ),
              ),
            ],
          ),
        ),
      ],
    );
  }
}

5-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);

  @override
  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),
            ),
          ),
        ),
      ],
    );
  }
}

5-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);

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

      // 方法二
      // 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-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);

  @override
  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,
          // 相对于容器SizedBox来定位
          child: Stack(
            children: const [
              Positioned(left: 10, child: Text('收藏')),
              Positioned(right: 10, child: Text('购买')),
            ],
          ),
        ),
      ],
    );
  }
}

6 比例组件

  • 作用是根据设置调整子元素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);

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

7 卡片组件

  • Card是卡片组件块,内容由大多数类型组件构成,具有圆角和阴影,看起来更具立体感。
  • margin(外边距)、elevation(阴影值的深度)、color(背景颜色)、shadowColor(阴影颜色)。
  • child(子组件)、Shape(阴影效果,默认为圆角长方形边)、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);

  @override
  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('地址:福建省厦门市湖里区')),
            ],
          ),
        ),
      ],
    );
  }
}

7-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);

  @override
  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(
                  'https://gimg2.baidu.com/image_search'
                  '/src=http%3A%2F%2Fc-ssl.duitang.com%'
                  '2Fuploads%2Fitem%2F201905%2F19%2F201'
                  '90519103932_lCZT3.jpeg&refer=http%3A'
                  '%2F%2Fc-ssl.duitang.com&app=2002&siz'
                  'e=f9999,10000&q=a80&n=0&g=0n&fmt=aut'
                  'o?sec=1725157682&t=1c8606884ee741fc8'
                  '5920124947c8969',
                  fit: BoxFit.cover,
                ),
              ),
              ListTile(
                // ClipOval实现圆形图片
                leading: ClipOval(
                  child: Image.network(
                    'https://img0.baidu.com/it/u=377968'
                    '1995,915534195&fm=253&fmt=auto&app'
                    '=138&f=JPEG?w=500&h=500',
                    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(
                  'https://img0.baidu.com/it'
                  '/u=2050583615,2484881629&'
                  'fm=253&fmt=auto&app=138&f'
                  '=JPEG?w=890&h=500',
                  fit: BoxFit.cover,
                ),
              ),
              const ListTile(
                // CircleAvatar实现圆形图片
                leading: CircleAvatar(
                  // 设置头像半径
                  radius: 25,
                  backgroundImage: NetworkImage(
                    'https://gimg2.baidu.com/image_search'
                    '/src=http%3A%2F%2Fb-ssl.duitang.com%'
                    '2Fuploads%2Fitem%2F201803%2F23%2F201'
                    '80323164300_ZQLAt.jpeg&refer=http%3A'
                    '%2F%2Fb-ssl.duitang.com&app=2002&siz'
                    'e=f9999,10000&q=a80&n=0&g=0n&fmt=aut'
                    'o?sec=1725157893&t=24cf15995121c713c'
                    '6081a03a3596499',
                  ),
                ),
                title: Text('蒹葭'),
                subtitle: Text('蒹葭苍苍,白露为霜。'),
              ),
            ],
          ),
        ),
      ],
    );
  }
}

7-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(
              // ClipOval实现圆形图片
              leading: ClipOval(
                child: Image.network(
                  value['imageUrl'],
                  fit: BoxFit.cover,
                  height: 50,
                  width: 50,
                ),
              ),
              title: Text(value['title']),
              subtitle: Text(value['author']),
            ),
          ],
        ),
      );
    });
    return tempList.toList();
  }

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

8 按钮组件

  • 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);

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: [
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            // 凸起按钮
            ElevatedButton(
              // 打印ElevatedButton
              onPressed: () {
                print('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('普通按钮'),
            ),
          ],
        ),
      ],
    );
  }
}

8-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);

  @override
  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('搜索'),
              ),
            ),
          ],
        ),
      ],
    );
  }
}

8-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);

  @override
  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('登录'),
                ),
              ),
            ),
          ],
        ),
      ],
    );
  }
}

8-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);

  @override
  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('圆角'),
            ),
          ],
        ),
      ],
    );
  }
}

8-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);

  @override
  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('带边框的按钮'),
            ),
          ],
        ),
      ],
    );
  }
}

9 Wrap组件

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

9-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);

  @override
  Widget build(BuildContext context) {
    // return Button('第1集', onPressed: (){});
    return Padding(
      padding: const EdgeInsets.all(10),
      child: Wrap(
        // 居中显示
        // alignment: WrapAlignment.center,
        // x轴上的间距数(水平间距)
        spacing: 5,
        // y轴上的间距数(垂直间距)
        runSpacing: 10,
        // 垂直方向排列
        // 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);

  @override
  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),
    );
  }
}

9-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);

  @override
  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);

  @override
  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 4.0 许可协议。转载请注明来源 bsf !
评 论
  目录