30天计划第4天-Flutter核心概念:Widget

-
-
2025-10-22 12:28
  • 学习内容

    1. Flutter 核心思想:“一切皆 Widget”

    2. 两种基础 Widget:StatelessWidget(无状态,静态 UI)、StatefulWidget(有状态,动态 UI)

  • 实践任务

    1. StatelessWidget 写一个静态页面:显示 “我的第一个 Flutter 页面”+ 一张图片

    2. StatefulWidget 改写计数器 Demo:点击按钮数字 + 1,标题显示当前数字

一、Flutter核心思想:“一切皆Widget”

1. 什么是Widget

  • 核心定义:Widget是Flutter中UI的基本构建块,是“描述UI的配置信息”(不是实际渲染对象,实际渲染由RenderObject处理)。

  • 通俗理解:所有可见的(文本、图片、按钮)和不可见的(布局、动画控制器)元素都是Widget,通过组合Widget形成完整页面。

2. “一切皆Widget”的具体体现

  • 可见元素:Text(文本)、Button(按钮)、Image(图片)、Container(容器)等

  • 布局结构:Row(水平布局)、Column(垂直布局)、Stack(层叠布局)等

  • 样式控制:Padding(内边距)、Margin(外边距,通过Container的margin属性)、FontSize(字体大小,通过TextStyle)等

  • 交互逻辑:GestureDetector(手势检测)、InkWell(点击水波纹)等

import 'package:flutter/material.dart';

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

// 根Widget:整个应用的入口
class MyApp extends StatelessWidget
{
  @override
  Widget build(BuildContext context)
  {
    // MaterialApp:提供Material Design风格的基础配置(标题、主题等)
    return MaterialApp(
      title: '一切皆Widget',
      home: Scaffold(  // Scaffold:页面内基础结构(包含AppBar、body等)
        appBar: AppBar( // AppBar:顶部导航栏
          title: Text('Widget示例'), // Text:文本Widget
        ),
        body: Center( // Center:居中布局Widget
          child: Container( // Container:容器Widget(可设置宽高、颜色、边距等)
            width: 200,
            height: 100,
            color: Colors.yellow,
            child: Text(
              '我是文本',
              style: TextStyle( // TextStyle:文本样式Widget
                color: Colors.white,
                fontSize: 20,
              ),
            ),
          ),
        )
      )
    );
  }
}

运行效果如下(基于Edge浏览器):

二、StatelessWidget:无状态组件

1. 核心特性

  • 无状态:创建后属性不可变(内部状态不会变化),UI是静态的。
  • 生命周期简单:只有一个build方法,初始化时调用一次,之后不会重新构建(除非父Widget重建)。
  • 适用场景:展示固定内容(如:标题、静态图片、固定文本的卡片)。

2. 如何创建StatelessWidget

(1)定义一个类继承StatelessWidget

(2)重写build方法(返回一个Widget,描述UI结构)

(3)通过构造函数接收外部参数(通常用final修饰,确保不可变)

import 'package:flutter/material.dart';

void main() => runApp(
  MaterialApp(
    home: Scaffold(
      body: StatelessDemo(
        title: 'Flutter基础',
        content: 'StatelessWidget是无状态组件,UI不会自动变化',
      )
    )
  )
);

// 无状态组件:展示固定信息的卡片
class StatelessDemo extends StatelessWidget
{
  // 用final修饰属性,确保不可改变
  final String title;
  final String content;
  final Color color;

  // 构造函数:接收外部参数(required表示必传)
  const StatelessDemo({
    super.key,
    required this.title,
    required this.content,
    this.color = Colors.yellow, // 默认值
  });

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.all(16), // 内边距Widget
      child: Container(
        padding: EdgeInsets.all(12),
        decoration: BoxDecoration( // 装饰Widget(设置背景、圆角等)
          color: color,
          borderRadius: BorderRadius.circular(8)
        ),
        child: Column( // 垂直布局Widget
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              title,
              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
            SizedBox(height: 18), // 间距Widget
            Text(content),
          ],
        ),
      ),
    );
  }
}

运行效果如下:

三、StatefulWidget:有状态组件

1. 核心特性

  • 有状态:内部有可变状态(State),状态变化时UI会重新渲染。
  • 生命周期复杂:包含initState(初始化)、build(构建UI)、setState(更新状态)、dispose(销毁)等阶段。
  • 适用场景:需要动态更新的UI(如:计数器、表单输入、切换显示内容)。

2. 如何创建StatefulWidget

  • 定义一个类继承StatefulWidget(通常作为“壳”,不存储状态)
  • 定义一个对应的State类(继承State<MyWidget>),存储可变状态
  • 在State类中重写build方法,通过setState更新状态(触发UI重建)

(1)示例1:基础计数器

import 'package:flutter/material.dart';

void main() => runApp(MaterialApp(
  home: Scaffold(
    body: CounterDemo(),
  ),
));

// 有状态组件的“壳”
class CounterDemo extends StatefulWidget{
  const CounterDemo({super.key});

  @override
  State<CounterDemo> createState() => _CounterDemoState();
}

// 状态类:存储可变状态,处理逻辑
class _CounterDemoState extends State<CounterDemo> {
  // 定义可变状态(计数器值)
  int _count = 0;

  // 初始化方法:组件创建时调用一次
  @override
  void initState() {
    super.initState();
    
    // 生产环境不建议用print
    print('计数器初始化');
  }

  // 构建UI:状态变化时会重新调用
  @override
  Widget build(BuildContext context) {
    print('UI重新构建,当前计数:$_count');
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(
            '当前计数:$_count',
            style: TextStyle(fontSize: 24),
          ),
          SizedBox(height: 16),
          ElevatedButton( // 按钮Widget(可点击)
            onPressed: () {
              // 用setState更新状态:触发build方法重新执行,UI更新
              setState(() {
                _count++;
              });
            },
            child: Text('点击加1'),
          )
        ],
      ),
    );
  }

  // 组件销毁时调用(如:页面关闭)
  @override
  void dispose() {
    super.dispose();
    print('计数器销毁');
  }
}

运行效果如下:

(2)示例2:动态切换文本

 import 'package:flutter/material.dart';

void main() => runApp(MaterialApp(home: Scaffold(body: ToggleTextDemo())));

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

  @override
  State<ToggleTextDemo> createState() => _ToggleTextDemoState();
}

class _ToggleTextDemoState extends State<ToggleTextDemo> {
  // 状态1:是否显示第一句文本
  bool _showFirstText = true;

  // 状态2:文本颜色
  Color _textColor = Colors.yellow;

  // 切换文本内容
  void _toggleText() {
    setState(() {
      _showFirstText = !_showFirstText;
    });
  }

  // 切换文本颜色
  void _changeColor() {
    setState(() {
      _textColor = _textColor == Colors.yellow ? Colors.red : Colors.yellow;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.all(16),
      child: Column(
        children: [
          Text(
            // 根据状态显示不同的文本
            _showFirstText ? '我是第一句' : '我是第二句',
            style: TextStyle(fontSize: 20, color: _textColor),
          ),
          SizedBox(height: 16),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: _toggleText,
                child: Text('切换文本'),
              ),
              SizedBox(width: 16),
              ElevatedButton(onPressed: _changeColor, child: Text('切换颜色'))
            ],
          )
        ],
      ),
    );
  }
}

运行效果:

四、Stateless与Stateful对比

核心对照区别表

特性StatelessWidgetStatefulWidget
状态是否可变不可变(属性用final修饰)可变(状态存储在State类中)
重建触发条件仅父Widget重建时调用setState()或父Widget重建时
适用场景静态UI(固定文本、图片)动态UI(计数器、表单、切换内容)
生命周期方法只有build()initState()、build()、dispose()等

五、综合实战

需求:创建一个页面,包含:

  • 顶部静态标题
  • 中间动态计数器
  • 底部静态说明文本
import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('widget组合示例')),
        body: Column(
          children: [
            // 静态标题(Stateless)
            StaticTitle(title: '今日学习进度'),

            // 动态计数器
            Expanded(child: StudyCounter()),

            // 静态说明(Stateless)
            StaticDescription(
              text: '提示:点击按钮增加学习次数,每天坚持!'
            )
          ],
        ),
      ),
    );
  }
}

// 静态标题组件(Stateless)
class StaticTitle extends StatelessWidget {
  final String title;
  const StaticTitle({super.key, required this.title});

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.all(16),
      child: Text(
        title,
        style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
      ),
    );
  }
}

// 动态计数器组件(Stateful)
class StudyCounter extends StatefulWidget {
  const StudyCounter({super.key});

  @override
  State<StudyCounter> createState() =>  _StudyCounterState();
}

class _StudyCounterState extends State<StudyCounter> {
  int _studyCounter = 0;

  void _increment() => setState(() {
    _studyCounter++;
  });

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(
            '已学习$_studyCounter次',
            style: TextStyle(fontSize: 24, color: Colors.yellow),
          ),
          SizedBox(height: 16),
          ElevatedButton(onPressed: _increment, child: Text('点击完成一次学习'))
        ],
      ),
    );
  }
}

// 静态说明组件(Stateless)
class StaticDescription extends StatelessWidget {
  final String text;

  const StaticDescription({super.key, required this.text});

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.all(12),
      child: Text(
        text,
        style: TextStyle(color: Colors.yellow[600]),
      ),
    );
  }
}

运行效果如下:


目录