Flutter学习笔记(四)---基础Widget

编程范式

以下从核心思想、特点、典型场景/语言角度:

一、基础分类:命令式 vs 声明式(最顶层分类)

  • 命令式编程(核心:“怎么做”)

    • 思想:像给计算机“一步步下指令”,详细描述实现目标的步骤
    • 特点:关注过程,你得告诉计算机“先做A,再做B,最后做C”。
    • 典型代表:面向过程编程(如 C 语言写算法步骤)、大部分传统语言(Python/Java 写循环逻辑也算命令式)。
    • 例子:“计算 1+2+…+100”,命令式会写 sum=0; for(i=1;i<=100;i++){ sum+=i },一步步教计算机求和。
  • 声明式编程(核心:“是什么”)

    • 思想:描述目标的结果/性质,不关心具体怎么实现,让计算机自己找方法。
    • 特点:关注结果,你说“我要一个这样的东西”,计算机自己解决“怎么做”。
    • 典型代表:SQL(写 SELECT * FROM table WHERE id=1,不用管数据库怎么查)、React(JSX 描述 UI 长啥样,框架自己渲染)、函数式编程里的高阶抽象。
    • 例子:还是“计算 1+2+…+100”,声明式可能写 sum(range(1,101))(Python 的 sum 函数内部帮你循环求和,你只声明“求这个范围的和” )。

二、细分范式(基于命令式/声明式衍生)

  • 面向对象编程(OOP)

    • 归属:命令式编程的细分(因为写代码时还是会一步步调对象方法),但思想更关注“对象交互”。
    • 核心:把程序拆成“对象”(有属性和方法),通过对象之间的消息传递/方法调用解决问题。
    • 特点:强调封装、继承、多态,适合复杂业务逻辑(如 Java 写电商系统,订单、用户都是对象)。
    • 对比命令式:命令式可能平铺直叙写步骤,OOP 则把步骤封装到对象里,让对象自己管理状态。
  • 函数式编程(FP)

    • 归属:既可以是命令式(如 Python 的函数调用),也能融入声明式(如 Haskell 纯函数式),核心是“函数为一等公民”。
    • 核心:用纯函数(无副作用、输入定则输出定) 组合解决问题,避免 mutable 状态。
    • 特点:强调 immutable 数据、函数可作为参数/返回值,适合复杂数据处理(如 Scala 处理大数据流)。
    • 对比 OOP:OOP 用对象存状态+方法改状态,FP 尽量不用可变状态,用函数嵌套/组合算结果。
  • 面向过程编程

    • 归属:命令式编程的最直接体现,完全按“步骤化”写代码。
    • 核心:把程序拆成过程(函数/步骤),按顺序执行,数据在过程间传递。
    • 特点:简单直接,但复杂项目里代码容易变“面条式”(逻辑纠缠)。
    • 典型:C 语言写算法(如排序算法,一步步写交换、比较逻辑 )。
  • 面向协议编程(POP,也叫面向接口编程)

    • 归属:常和 OOP 结合,更关注“协议/接口”而非具体实现。
    • 核心:定义协议(接口) 规定“必须有哪些行为”,具体对象实现协议,程序依赖协议而非具体类。
    • 特点:解耦性强,适合框架开发(如 Swift 的 Protocol,Java 的 Interface )。
    • 对比 OOP:OOP 可能直接依赖子类,POP 依赖协议,替换实现更灵活(如把“支付接口”换成微信/支付宝,只要实现协议即可 )。

三、一句话总结

  • 最顶层分 命令式(教计算机一步步做)声明式(告诉计算机想要什么结果)
  • 面向对象、面向过程、面向协议,都是命令式的细分(只是组织代码的方式不同);函数式编程可跨命令式/声明式,但更偏向声明式的简洁。
  • 实际开发中,很多语言/项目是多范式混合(如 Python 同时支持 OOP、函数式、命令式 ),不用死抠“必须属于某一种”,理解核心思想即可~

以下是 Objective-C、Swift、SwiftUI、Flutter、Vue、Java、Python 等编程语言或框架所涉及的编程范式:

Objective-C

  • 面向对象编程:Objective-C 是一种面向对象的编程语言,它以类和对象为基础。通过定义类来封装数据和方法,使用继承来实现代码的复用,通过多态来实现不同对象对同一消息的不同响应。例如,在开发 iOS 应用时,创建视图控制器类继承自 UIViewController,然后添加自己的属性和方法来实现特定的功能。
  • 命令式编程:在 Objective-C 中,开发者也会采用命令式编程的方式,按照顺序编写一系列的语句来实现具体的功能。比如在处理网络请求时,会依次编写建立连接、发送请求、处理响应等步骤的代码。

Swift

  • 面向对象编程(OOP):Swift 支持类、继承、封装和多态等面向对象的特性。开发者可以定义类,通过类的实例来处理数据和执行操作,例如创建一个 Person 类,包含属性(如姓名、年龄)和方法(如打招呼),子类可以继承父类的属性和方法并进行重写 。
  • 函数式编程(FP):Swift 提供了许多函数式编程的特性,比如闭包(Closure)。闭包是自包含的代码块,可以在代码中被传递和使用,类似于函数式编程中的函数。同时,Swift 的集合类型(如 ArrayDictionary)提供了一系列高阶函数,像 mapfilterreduce 等,方便对集合中的元素进行操作,体现了函数式编程中数据不可变以及函数作为一等公民的思想。
  • 面向协议编程(POP):这是 Swift 的一个重要编程范式。协议定义了一组方法、属性等要求,类型(类、结构体、枚举)通过遵循协议来实现这些要求。比如可以定义一个 Drawable 协议,规定实现该协议的类型必须具备绘制自身的方法,然后 RectangleCircle 结构体可以遵循这个协议并实现绘制方法。这使得代码具有更好的扩展性和解耦性。

SwiftUI

  • 声明式编程:SwiftUI 主要采用声明式编程范式。开发者通过描述界面的最终状态,而不是一步步地命令式地构建界面。例如,定义一个按钮,只需要声明它的标题、样式以及点击后的行为,SwiftUI 框架会自动处理界面的渲染和更新。当数据发生变化时,SwiftUI 会根据声明的规则自动更新界面,不需要开发者手动去更新每一个视图元素。

Flutter

  • 声明式编程:Flutter 使用 Dart 语言,其核心是声明式 UI 构建。开发者通过 Widget 来描述用户界面,当状态发生变化时,Flutter 会根据新的状态重新构建 Widget 树,从而更新界面。例如,定义一个 Text Widget 和一个 Button Widget,当点击按钮改变数据时,相关的 Widget 会自动更新显示。
  • 函数式编程:Dart 语言也支持函数式编程的一些特性,比如使用闭包来处理事件、对集合进行操作等。在 Flutter 开发中,闭包常用于处理按钮点击事件等场景 。

Vue

  • 声明式编程:Vue 是一个渐进式的前端框架,采用声明式渲染。开发者通过编写 Vue 模板(或单文件组件中的模板部分)来描述视图结构,通过 Vue 的响应式系统,当数据发生变化时,视图会自动更新。例如,在模板中绑定数据到 HTML 元素,当数据改变时,对应的元素内容会自动更新。
  • 组件化编程:这虽然不是传统意义上的编程范式,但在 Vue 开发中非常重要。Vue 允许将界面拆分成一个个独立的组件,每个组件有自己的状态和方法,组件之间可以通过 props 和事件进行通信,这有助于提高代码的复用性和可维护性。

Java

  • 面向对象编程:Java 是一门典型的面向对象编程语言,全面支持类、对象、继承、封装、多态等面向对象的概念。在企业级应用开发中,会创建大量的类来表示业务实体和逻辑,通过对象之间的交互来完成各种功能,比如在电商系统中,会有 Product 类、Order 类等。
  • 命令式编程:Java 代码中也包含大量的命令式编程风格,比如使用 for 循环、while 循环来处理数据,按照顺序执行一系列的语句来完成任务。
  • 函数式编程:自从 Java 8 引入了 lambda 表达式和流(Stream)API 后,Java 也具备了一些函数式编程的能力。通过流 API 可以对集合进行 mapfilterreduce 等操作,lambda 表达式可以方便地定义匿名函数。

Python

  • 命令式编程:Python 可以很自然地编写命令式代码,通过顺序执行语句、使用条件判断和循环结构来实现功能。例如,使用 for 循环遍历列表,对每个元素进行操作。
  • 面向对象编程:Python 支持面向对象编程,能够定义类和对象,实现继承、封装和多态。在开发大型项目时,通过类来组织代码,提高代码的可维护性和复用性。
  • 函数式编程:Python 提供了一些函数式编程的特性,比如函数可以作为参数传递、返回值返回,支持 lambda 表达式(匿名函数),还提供了 mapfilterreduce 等函数,方便对数据进行处理。此外,Python 的装饰器也是函数式编程思想的一种体现。
  • 脚本式编程:Python 也常被用于脚本编写,快速实现一些自动化任务,它简洁的语法和动态类型系统使得脚本编写非常便捷。

这些编程语言和框架在实际应用中,往往会融合多种编程范式,以适应不同的开发场景和需求。


Text

在这里插入图片描述

class YZHomePage extends StatelessWidget {
  const YZHomePage({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("基础组件"),
      ),
      body: Text(
          "黄色的树林里分出两条路,可惜我不能同时去涉足,我在那路口久久伫立,我向着一条路极目望去,直到它消失在丛林深处",
          textAlign: TextAlign.center,
          maxLines: 1,///最大行数
          overflow: TextOverflow.ellipsis,///如果被省略了,省略号结束
          textScaleFactor: 0.5,///缩放因子
          style: TextStyle(
              fontSize: 25,
              color: Colors.pink,
              fontWeight: FontWeight.bold
          ),
      ),
    );
  }
}

富文本

在这里插入图片描述

import 'package:flutter/material.dart';

main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: YZHomePage(),
    );
  }
}

class YZHomeButtonPage extends StatelessWidget {
  const YZHomeButtonPage({super.key});

  
  Widget build(BuildContext context) {
    return const Placeholder();
  }
}


class YZHomePage extends StatelessWidget {
  const YZHomePage({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("基础组件"),
      ),
      body: YZHomeTextContent()
    );
  }
}

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

  
  State<YZHomeTextContent> createState() => _YZHomeTextContentState();
}

class _YZHomeTextContentState extends State<YZHomeTextContent> {
  
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(
          "黄色的树林里分出两条路,可惜我不能同时去涉足,我在那路口久久伫立,我向着一条路极目望去,直到它消失在丛林深处",
          textAlign: TextAlign.center,
          maxLines: 3,///最大行数
          overflow: TextOverflow.ellipsis,///如果被省略了,省略号结束
          textScaleFactor: 1.2,///缩放因子
          style: TextStyle(
              fontSize: 25,
              color: Colors.yellow,
              fontWeight: FontWeight.bold
          ),
        ),

        /// 符文本
        Text.rich(
            TextSpan(
              text: "Hello OC",
              style: TextStyle(color: Colors.green, fontSize: 28),
            )
        ),

        Text.rich(
            TextSpan(
              /// 符文本有多个
              children: [
                TextSpan(
                  text: "Hello Swift",
                  style: TextStyle(color: Colors.yellow, fontSize: 15),
                ),
                TextSpan(
                  text: "Hello SwiftUI",
                  style: TextStyle(color: Colors.pink, fontSize: 15),
                ),
                WidgetSpan(child: Icon(Icons.face, color: Colors.lightBlueAccent,)),
                TextSpan(
                  text: "Hello Flutter",
                  style: TextStyle(color: Colors.purple, fontSize: 15),
                ),
              ],

            )
        ),
      ],
    );
  }
}

按钮

在这里插入图片描述

class _YZHomeButtonContentState extends State<YZHomeButtonContent> {
  
  Widget build(BuildContext context) {
    return Column(
      children: [
        ElevatedButton(
          onPressed: (){
            print("ElevatedButton");
          },
          style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Colors.green)),
          child: Text("ElevatedButton", style: TextStyle(color: Colors.red)),
        ),
        TextButton(
            onPressed: (){
              print("TextButton");
            },
            child: Text("TextButton")
        ),
        OutlinedButton(
            onPressed: (){
              print("OutlinedButton");
            },
            child: Text("OutlinedButton")
        ),

        ///自定义按钮
        TextButton(
            onPressed: (){
              print("TextButton");
            },
            child: Row(
              /// 默认是占宽度全屏,加上下面这句,就占本身大小
              mainAxisSize: MainAxisSize.min,
              children: [
                Icon(Icons.favorite),
                Text("I Love You!")
              ],
            ),
            style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Colors.cyan)),
        ),
      ],
    );
  }
}

图片

在这里插入图片描述

class _YZHomeImageContentState extends State<YZHomeImageContent> {
  
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 网络图片
        Image(image: NetworkImage("https://profile-avatar.csdnimg.cn/6048fbe7bbe8476182ccb4e1364f988f_iosshan.jpg!1")),
        
        // 本地图片 - 添加错误处理
        Image.asset(
          color: Colors.red,
          colorBlendMode: BlendMode.colorBurn,
          "assets/images/1211.png",
          width: 200,
          errorBuilder: (context, error, stackTrace) {
            return Container(
              width: 200,
              height: 100,
              color: Colors.grey[300],
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Icon(Icons.error, color: Colors.red),
                  Text("图片加载失败", style: TextStyle(color: Colors.red)),
                  Text("请检查 assets/images/1211.png 是否存在", style: TextStyle(fontSize: 12)),
                ],
              ),
            );
          },
        ),
        
        // 使用 AssetImage 的方式
        Image(
          color: Colors.yellow,
          colorBlendMode: BlendMode.difference,
          image: AssetImage("assets/images/1211.png"),
          width: 200,
          errorBuilder: (context, error, stackTrace) {
            return Container(
              width: 200,
              height: 100,
              color: Colors.grey[300],
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Icon(Icons.error, color: Colors.red),
                  Text("AssetImage 加载失败", style: TextStyle(color: Colors.red)),
                  Text("错误: $error", style: TextStyle(fontSize: 10)),
                ],
              ),
            );
          },
        ),
      ],
    );
  }
}

按钮紧挨着、placehold图片、Icon、输入框

在这里插入图片描述

class _YZHomeTextContentState extends State<YZHomeTextContent> {

  final usernameController = TextEditingController();
  final passwordController = TextEditingController();

  
  Widget build(BuildContext context) {
    return Column(
      // 1. 消除 Column 子组件之间的默认垂直间距
      //mainAxisSize: MainAxisSize.min, // 让 Column 仅占用子组件所需空间
      children: [
        TextButton(
          onPressed: (){
            
          }, 
          child:Text("TextButton1"),
          style: ButtonStyle(
            backgroundColor: MaterialStateProperty.all(Colors.lightBlueAccent),
            // 去除按钮的外边距(默认可能存在微小外边距,这里确保彻底清除)
            tapTargetSize: MaterialTapTargetSize.shrinkWrap,
          ),

        ),

        TextButton(
          onPressed: (){

          },
          child:Text("TextButton2"),
          style: ButtonStyle(
            backgroundColor: MaterialStateProperty.all(Colors.lightGreen),
            // 去除按钮的外边距(默认可能存在微小外边距,这里确保彻底清除)
            tapTargetSize: MaterialTapTargetSize.shrinkWrap,
          ),
        ),

        /// 没有文字的按钮,不显示
        TextButton(
          onPressed: (){

          },
          child:Text(""),
          style: ButtonStyle(
            backgroundColor: MaterialStateProperty.all(Colors.lightGreen),
            // 去除按钮的外边距(默认可能存在微小外边距,这里确保彻底清除)
            tapTargetSize: MaterialTapTargetSize.shrinkWrap,

            /// 下面两个属性设置,将没有文字的按钮大小,设置为0
            // 移除最小宽度和高度
            minimumSize: MaterialStateProperty.all(Size.zero),
            // 移除内边距
            padding: MaterialStateProperty.all(EdgeInsets.zero),
          ),
        ),

        /// 占位图
        FadeInImage(
          placeholder:AssetImage("assets/images/1211.png"),
          image: NetworkImage("https://profile-avatar.csdnimg.cn/6048fbe7bbe8476182ccb4e1364f988f_iosshan.jpg!1"),
        ),

        /// Icon
        Icon(
          Icons.pets,
          color: Colors.lightGreen,
          size: 120,
          shadows: [
            Shadow(
                color: Colors.black26,
                offset: Offset(5, 5)
            )
          ],
        ),

        Padding(
          padding: EdgeInsets.all(10),
          ///TextField
          child: TextField(
            decoration: InputDecoration(
                icon: Icon(Icons.face),
                iconColor: Colors.greenAccent,
                labelText: "请输入内容:",
                border: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey, width: 1))
            ),
            onChanged: (value) {
                print("输入内容: $value");
            },
            onSubmitted: (value) {
              print("提交内容: $value");
            },
            controller: usernameController,
          ),
        ),

        Padding(
          padding: EdgeInsets.all(10),
          ///TextField
          child: TextField(
              decoration: InputDecoration(
                  icon: Icon(Icons.lock),
                  iconColor: Colors.greenAccent.withOpacity(0.8),
                  labelText: "请输入密码:",
                  border: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey, width: 1))
              ),
              controller: passwordController,
          ),
        ),

        Container(
            width: 320,
            height: 44,
            child: TextButton(
              onPressed: (){
                final username = usernameController.text;
                final password = passwordController.text;
                print("账户:$username, 密码:$password");
              },
              child: Text("登 录", style: TextStyle(color: Colors.white),),
              style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Colors.purple)),
            )
        )


      ],
    );
  }
}

Icon 在这里插入图片描述


class _YZHomeTextContentState extends State<YZHomeTextContent> {

  
  Widget build(BuildContext context) {
    return Container(
        width: 300,
        height: 200,
        child: Align(
          alignment: Alignment(0.7, 0.6),//中心点是(0,0)
          child: Transform.rotate(
            angle: math.pi/(3),
            child: Icon(
              Icons.pets,
              size: 50,
              color: Colors.red,
            ),
          )
        ),
        decoration: BoxDecoration(
          color: Colors.yellow,
          borderRadius: BorderRadius.all(
              Radius.elliptical(10, 10)
          ),
        ),
    );
  }
}


布局

在这里插入图片描述

mainAxisAlignment: MainAxisAlignment.spaceEvenly,

问:flutter里面的 Flexible以及Expanded的用法

在 Flutter 中,FlexibleExpanded 都是用于在 RowColumnFlex 等弹性布局中控制子组件尺寸的重要组件,它们能帮助子组件根据可用空间动态调整大小。

核心概念:弹性布局(Flex)

RowColumn 本质上是 Flex 组件的特殊形式(Row 是水平方向的 FlexColumn 是垂直方向的 Flex)。弹性布局的核心是通过 flex 属性分配可用空间:

  • 当父组件有剩余空间时,flex 值越大的子组件,分到的空间越多;
  • 当空间不足时,会优先压缩 flex 值较小的组件(或未设置 flex 的组件)。

1. Flexible

Flexible 允许子组件在可用空间内灵活调整尺寸,但不会强制子组件填满剩余空间,即子组件可以“按需收缩或扩展”,但不超过自身最大/最小限制。

常用属性:
  • flex:整数,默认值为 1,用于分配剩余空间的比例(类似权重);
  • fit:控制子组件如何适应可用空间,有两个值:
    • FlexFit.loose(默认):子组件尺寸不超过自身最大限制(按需扩展,不强制填满);
    • FlexFit.tight:子组件必须填满分配到的空间(强制扩展)。
示例:
Row(
  children: [
    Container(width: 50, height: 50, color: Colors.red),
    // 剩余空间按 1:2 分配给两个 Flexible
    Flexible(
      flex: 1,
      fit: FlexFit.loose, // 默认,不强制填满
      child: Container(height: 50, color: Colors.green),
    ),
    Flexible(
      flex: 2,
      fit: FlexFit.tight, // 强制填满分配的空间
      child: Container(height: 50, color: Colors.blue),
    ),
  ],
)
  • 红色容器固定宽度 50;
  • 剩余空间中,绿色占 1 份(loose 模式,宽度不超过自身内容或限制),蓝色占 2 份(tight 模式,强制填满分配的空间)。

在这里插入图片描述

2. Expanded

ExpandedFlexible子类,它等价于 Flexible(fit: FlexFit.tight),即强制子组件填满分配到的所有可用空间

常用属性:
  • flex 属性(默认值 1),功能与 Flexibleflex 一致。
示例:
Row(
  children: [
    Container(width: 50, height: 50, color: Colors.red),
    // 等价于 Flexible(flex:1, fit: FlexFit.tight)
    Expanded(
      flex: 1,
      child: Container(height: 50, color: Colors.green),
    ),
    Expanded(
      flex: 2,
      child: Container(height: 50, color: Colors.blue),
    ),
  ],
)
  • 红色容器固定宽度 50;
  • 剩余空间按 1:2 分配给绿色和蓝色容器,且两者都会强制填满分配到的空间(宽度随父组件变化)。

关键区别

特性FlexibleExpanded
本质基础弹性组件Flexible 的子类(fit: tight
空间填充可选(loose 不强制,tight 强制)强制填满分配的空间
适用场景子组件尺寸需保留上限(如文本换行)子组件需占满剩余空间(如列表项)

典型使用场景

  1. Expanded 适用场景

    • 列表项中需要占满剩余宽度的文本或按钮;
    • 平分空间的布局(如三个按钮平均占满一行)。
    // 三个按钮平分一行
    Row(
      children: [
        Expanded(child: ElevatedButton(onPressed: () {}, child: Text("按钮1"))),
        Expanded(child: ElevatedButton(onPressed: () {}, child: Text("按钮2"))),
        Expanded(child: ElevatedButton(onPressed: () {}, child: Text("按钮3"))),
      ],
    )
    
  2. Flexible 适用场景

    • 文本内容可能过长,需要自动换行(loose 模式避免文本被强制拉伸);
    • 子组件有最大尺寸限制,不希望超过该限制。
    // 文本过长时自动换行,不强制占满空间
    Row(
      children: [
        Container(width: 80, height: 40, color: Colors.grey),
        Flexible(
          child: Text(
            "这是一段可能很长的文本,Flexible会让它在剩余空间内换行,而不是被强制拉伸",
            softWrap: true,
          ),
        ),
      ],
    )
    

总结

  • ExpandedFlexible(fit: FlexFit.tight) 的简写,强制子组件填满分配的空间,适合需要占满剩余空间的场景;
  • Flexible 更灵活,通过 fit 控制子组件是否强制填满空间,适合子组件有尺寸限制或需要保留自然大小的场景;
  • 两者都通过 flex 属性分配空间比例,flex 值越大,分到的空间越多。

Stack

在这里插入图片描述

class _YZHomeStackContentState extends State<YZHomeStackContent> {
  
  Widget build(BuildContext context) {
    return Stack(
      alignment: Alignment.bottomRight,//默认左上角
      // fit: StackFit.expand,//将所以子元素拉伸最大化
      children: [
        Image(image: AssetImage("assets/images/1211.png"), height: 500,),
        Container(
          height: 200,
          width: 200,
          color: Colors.greenAccent,
        ),
        Positioned(
            right: 10,
            bottom: 10,
            child:Text("这是一个标题描述")
        )

      ],
    );
  }
}



滚动Widget

List

ListView

在这里插入图片描述

class _YZHomeListViewContentState extends State<YZHomeListViewContent> {
  
  Widget build(BuildContext context) {
    return ListView(
      children: [
        Text("data"),
        Text("data2"),
        Text("data3"),
      ],
  }
}

List.generate

在这里插入图片描述

class _YZHomeListViewContentState extends State<YZHomeListViewContent> {
  
  Widget build(BuildContext context) {
    return ListView(
      children: List.generate(100, (index){
        //return Text("Hello World! $index", style: TextStyle(color: getRandomColor(), fontSize: 25));

        return ListTile(
          leading: Icon(Icons.people),
          trailing: Icon(Icons.delete),
          title: Text("联系人! $index", style: TextStyle(color: getRandomColor(), fontSize: 25)),
          subtitle: Text("180012000"),
        );
      })
    );
  }
}

Color getRandomColor() {
  final random = Random();
  return Color.fromARGB(
    random.nextInt(256), // 透明度 (0-255)
    random.nextInt(256), // 红色 (0-255)
    random.nextInt(256), // 绿色 (0-255)
    random.nextInt(256), // 蓝色 (0-255)
  );
}

ListView.builder

在这里插入图片描述

class _YZHomeListViewContentState extends State<YZHomeListViewContent> {
  
  Widget build(BuildContext context) {
    return ListView.builder(
        itemCount: 10,// cell个数
        itemExtent: 64,// 设置行高(不设置的话,内容填充)
        itemBuilder: (BuildContext context, int index){
          return Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text("Hello Word~ ${index + 1}", style: TextStyle(fontSize: 20, color: Colors.brown),),//忘记加return了,注意下
              Divider(height: 1,)
            ],
          );
        },
    );
  }
}

Flutter 中这三种创建 ListView 的方式核心区别在于数据生成方式性能表现,适用于不同的场景,具体分析如下:

1. 第一种:直接通过 children 传入固定列表(静态列表)

特点:
  • 直接声明子组件children 接收一个固定的 Widget 数组,组件数量和内容在编译时就已确定。
  • 一次性构建所有组件:无论屏幕是否显示,都会提前创建数组中的所有组件。
  • 性能:如果列表项数量少(比如少于 10 个),性能影响可忽略;但数量多(比如 100 个以上)时,会一次性占用大量内存,导致初始化卡顿。
适用场景:
  • 列表项数量少且固定(如底部导航菜单、设置页面的几个选项)。
  • 每个列表项的布局差异较大,无法通过循环统一生成(如混合文本、图片、按钮的复杂静态列表)。

2. 第二种:通过 List.generate 生成列表(半动态列表)

特点:
  • 动态生成组件数组:通过 List.generate 循环生成 Widget 数组,本质还是通过 children 传入,但避免了手动写重复代码。
  • 仍然一次性构建所有组件:虽然代码简洁,但和第一种方式一样,会提前创建全部 100 个组件,内存占用与第一种相同。
  • 优势:相比手动写 100 个 Text,代码更简洁,适合有规律的重复组件。
适用场景:
  • 列表项数量中等(10-50 个),且每个 item 布局相同(仅内容随 index 变化)。
  • 不适合数量过大(如 1000 个)的列表,否则会导致内存占用过高。

3. 第三种:ListView.builder(动态列表/高性能列表)

特点:
  • 懒加载机制:只构建当前屏幕可见(及附近少量)的组件,滚动时动态创建/销毁不可见的组件,大幅节省内存。
  • 需要指定 itemCount:告诉列表总共有多少项,用于计算滚动范围。
  • 性能最优:适合超长列表(如 1000+ 项),避免一次性加载所有组件导致的性能问题。
适用场景:
  • 列表项数量多(50+ 个) 或数量不固定(如从网络请求动态获取数据)。
  • 每个列表项布局相同,仅内容随 index 变化(如联系人列表、商品列表)。
  • 对性能敏感的场景(如首页列表、长列表滚动)。

总结:如何选择?

方式核心区别适用场景
ListView(children: [...])静态固定列表,一次性构建所有组件数量少(<10)、布局差异大的静态列表
ListView(children: List.generate(...))动态生成数组,但仍一次性构建数量中等(10-50)、布局规律的列表
ListView.builder懒加载,按需构建组件数量多(>50)、动态数据或长列表

最佳实践

  • 短列表用第一种,中等规律列表用第二种,长列表或动态数据必用第三种(ListView.builder),以保证性能。

快速插入包裹组件快捷键:option+enter

GridView

在这里插入图片描述

class _YZHomeGridViewContentState extends State<YZHomeGridViewContent> {
  
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 8),// 左右边框8间距
      child: GridView(
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 3,// 一行3个
              childAspectRatio: 1.5,// 宽1 高0.5
              crossAxisSpacing: 10, // 交叉轴间距(这里是左右间距)8
              mainAxisSpacing: 5,//主轴间距(在这里是上下间距)
          ),
          children: List.generate(100, (index){
            return Container(
              /// 随机颜色
              color: Color.fromARGB(255, Random().nextInt(255), Random().nextInt(255), Random().nextInt(255)),
            );
          })
      ),
    );
  }
}

GridView.builder

在这里插入图片描述

class _YZHomeGridViewBuildContentState extends State<YZHomeGridViewBuildContent> {
  
  Widget build(BuildContext context) {
    return GridView.builder(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 3,// 一行3个
          childAspectRatio: 1.5,// 宽1 高0.5
          crossAxisSpacing: 10, // 交叉轴间距(这里是左右间距)8
          mainAxisSpacing: 5,//主轴间距(在这里是上下间距)
        ),
        itemBuilder: (BuildContext context, int index){
          return Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text("Hello Word~ ${index + 1}", style: TextStyle(fontSize: 20, color: Colors.brown),),//忘记加return了,注意下
              Divider(height: 1,)
            ]
          );
          }
    );
  }
}

Slivers

运用Sliver放在自定义的CustomScrollView中,可以做到一个View很多List和Grid

安全区域

SafeArea: 安全区域

SafeArea(// 安全区域 
	child: YZHomeCustomScrollViewContent()
),

SliverSafeArea: 安全区域(安全区域也可以滑过去)

slivers: [
	SliverSafeArea(/// 安全区域(安全区域也可以滑过去)
		sliver: SliverGrid(
		),
	)
],

SliverPadding,可以滑过去

在这里插入图片描述

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

  
  State<YZHomeCustomScrollViewContent> createState() => _YZHomeCustomScrollViewContentState();
}

class _YZHomeCustomScrollViewContentState extends State<YZHomeCustomScrollViewContent> {
  
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: [
        SliverSafeArea(/// 安全区域(安全区域也可以滑过去)
          sliver: SliverPadding(// 这个间距,可以滑过去
            padding: EdgeInsets.all(10),
            sliver: SliverGrid(
              delegate: SliverChildBuilderDelegate(
                  (BuildContext context, int index) {
                      return Container(
                        /// 随机颜色
                        color: Color.fromARGB(255, Random().nextInt(255), Random().nextInt(255), Random().nextInt(255)),
                      );
                  },
                  childCount: 50
              ),
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 3,// 一行3个
                childAspectRatio: 1.5,// 宽1 高0.5
                crossAxisSpacing: 10, // 交叉轴间距(这里是左右间距)8
                mainAxisSpacing: 5,//主轴间距(在这里是上下间距)
              ),
            
            ),
          ),
        )
      ],
    );
  }
}

在这里插入图片描述

class _YZHomeCustomScrollViewContentState extends State<YZHomeCustomScrollViewContent> {
  
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: [
        SliverAppBar(
            backgroundColor: Colors.lightGreenAccent,
            expandedHeight: 300,
            flexibleSpace: FlexibleSpaceBar(
              title: Text("Hello World~"),
              background: Image(image: AssetImage("assets/images/1211.png"), fit: BoxFit.cover,),
            ),
            pinned: true,
        ),

        SliverGrid(
          delegate: SliverChildBuilderDelegate(
                  (BuildContext context, int index) {
                return Container(
                  /// 随机颜色
                  color: Color.fromARGB(255, Random().nextInt(255), Random().nextInt(255), Random().nextInt(255)),
                );
              },
              childCount: 9
          ),
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 3,// 一行3个
            childAspectRatio: 1.5,// 宽1 高0.5
            crossAxisSpacing: 10, // 交叉轴间距(这里是左右间距)8
            mainAxisSpacing: 5,//主轴间距(在这里是上下间距)
          ),

        ),

        SliverList(
            delegate: SliverChildBuilderDelegate(
                (BuildContext context, int index) {
                  return ListTile(
                    title: Text("Hello World $index"),
                  );
                },
                childCount: 20
            )
        ),

      ],
    );
  }
}

滚动的监听

在这里插入图片描述

controller

controller监听,可以设置初始化的默认值,也可以监听滚动的位置

NotificationListener

NotificationListener监听:什么时候开始滚动、什么时候结束滚动

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

main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: YZHomePage(),
    );
  }
}

class YZHomeButtonPage extends StatelessWidget {
  const YZHomeButtonPage({super.key});

  
  Widget build(BuildContext context) {
    return const Placeholder();
  }
}


class YZHomePage extends StatefulWidget {

  const YZHomePage({super.key});

  
  State<YZHomePage> createState() => _YZHomePageState();
}

class _YZHomePageState extends State<YZHomePage> {
  /// 自定义controller
  /// controller监听,可以设置初始化的默认值,也可以监听滚动的位置。
  ScrollController _controller = ScrollController(initialScrollOffset: 300);
  bool _isShowFloatingActionButton = false;

  /// NotificationListener 监听:什么时候开始滚动、什么时候结束滚动
  /// 在这里面添加监听的操作
  
  void initState() {
    // TODO: implement initState
    super.initState();

    //添加监听
    _controller.addListener((){
      //print("正在滚动。。。。滚动位置 ${_controller.offset}");

      setState(() {
        _isShowFloatingActionButton = (_controller.offset > 1000);
      });
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("123"),
        ),
        body: NotificationListener(
          child: ListView.builder(
            itemBuilder:(BuildContext context, int index){
              return ListTile(
                leading: Icon(Icons.people),
                title: Text("联系人 $index"),

                subtitle: Text("No. ${Random().nextInt(1000)}"),
              );
            },
            itemCount: 100,
            controller: _controller,
          ),
          onNotification: (ScrollNotification notification){
              if (notification is ScrollStartNotification) {
                  print("滚动开始");
              }else if (notification is ScrollUpdateNotification) {
                print("正在滚动 总滚动距离 ${notification.metrics.maxScrollExtent}  当前滚动的位置: ${notification.metrics.pixels}");
              }else if (notification is ScrollEndNotification) {
                print("滚动结束");
              }
              return true;
         },
        ),
        floatingActionButton: _isShowFloatingActionButton ? FloatingActionButton(//浮窗按钮
          onPressed: (){
            print("FloatingActionButton");

            _controller.animateTo(0.0, duration: Duration(milliseconds: 100), curve: Curves.linear);
          },
          child: Icon(Icons.arrow_upward),
        ) : null,
    );
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值