详细标注文档: 点击查看文档

页面主组件

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(  // 应用程序根组件
    home: Scaffold( // 一种布局框架组件,实现多种效果的集合,可以选择性使用它的特性
        appBar: AppBar(
          centerTitle: true,
          backgroundColor: Color.fromARGB(255, 147, 177, 233),
          title: Text(
            '这是一个flutter Demo',
            textDirection: TextDirection.rtl,
            style: TextStyle(
                fontSize: 20, fontWeight: FontWeight.w800, color: Colors.blue),
          ),
        ),
        body: Normallifecycle()),
  ));
}

有状态组件

快捷键:stf +Tab
使用场景:数据驱动视图的场景,数据发生改变就使用有状态组件
编写示例如下:固定模板
在状态组件的母组件写变量并进行required能进行父传子并实时更新UI

class IAmStateFull extends StatefulWidget {
  const IAmStateFull({required xxx, super.key}); // 通过构造函数传毒数据 required表示参数必传
  final xxx = 1; // 定义父传子的参数
  @override
  State<IAmStateFull> createState() => _IAmStateFullState();
}

class _IAmStateFullState extends State<IAmStateFull> {
  @override
  Widget build(BuildContext context) {
    return const Placeholder();
  }
}

无状态组件

快捷键:stl+Tab
适合数据一次性渲染,没有变量变化的情况

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [const Text('我是一个无状态组件')],
      ),
    );
  }
}

class 类的介绍

  1. 最简单普通类
  2. new的时候无需传入参数
  3. Dart class中未显式声明任何构造函数时,会自动生成「隐式无参默认构造函数」;
    // 第一种:普通类无需参数初始化
    class NormalClass {
    NormalClass(); // 无参数构造函数 如果不写,默认会生成一个无参数的构造函数。
    static const String normalClass = ‘NormalClass’;
    }
  4. 需要参数传入的普通类
  5. 传入参数是为了new的时候初始化类里面的值
  6. 下面函数中的AbstraClass(string xxx)就是这个类的构造函数
    tips:如果之前学过js或者ts可以区别一下 js ts 构造函数是 constuctor
// 1.外界初始化class标注写法
class AbstractClass {
  String inputValue = 'InputValue'; // 给变量初始值
  AbstractClass(String xxxx) // 从外界传入初始值给InputValue
  {
    inputValue = xxxx;
  }

  /// 获取输入的值
  getInputValue() {
    return inputValue;
  }
}
  1. 传入参数初始化class语法糖写法(tips:语法糖,可以理解为为了便捷开发而定义的一种替代语法)
    1. 这里的语法糖是this
    2. 构造函数使用this就可以不用进行手动赋值 注意对比上面的普通写法
    tips:初学大部分情况下构造函数参数名和示例变量名相同就可以使用this,但是如果构造函数参数包含计算逻辑就不能使用。
// class外界初始化语法糖写法
class AbstractClass2 {
  String inputValue = 'InputValue'; // 给变量初始值
  AbstractClass2(this.inputValue); // this.写法是语法糖,其实就是简写
  /// 一个类的方法-获取输入的值
  getInputValue() {
    return inputValue;
  }
}
  1. 工具类创建方法
  2. 需要实现外界不能新new
  3. 只能向外提供方法及数据
  4. 构造函数中使用----> _
    1. flutter中使用_表示的是私有的意思
// 第三种:工具类
// 1. 工具类不需要实例化,所以构造函数使用了._ 的方式来定义,确保只能在类内部调用。
// 2. 工具类里面的方法都是静态方法,所以可以直接通过类名调用,不需要实例化。
class ToolClass {
  ToolClass._(); // 私有构造函数,防止外部实例化
  static const String toolClass = 'ToolClass';
}

/// 使用方法   因为使用了static 无需实例化即可拿到值
final toolxxx = ToolClass.toolClass;
Tab组件的使用
main.dart 应用主界面
知识点:
1. 如何定义初始化数据
2. 如何传递数据给Tab组件
import 'package:flutter/material.dart';
import 'package:flutter_basic_comp/comment/component/tab_custom_view.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '方天画戟的Flutter 组件 Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Comp Code 组件 Demo'),
    );
  }
}

// 页面主组件
class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  // 定义tab数据
  final List<ITabsListItem> _tabsList = [
    ITabsListItem('首页', Icons.home, '首页界面', color: Colors.blue),
    ITabsListItem('发现', Icons.find_replace_sharp, '发现界面', color: Colors.purple),
    ITabsListItem('消息', Icons.message, '消息界面'),
    ITabsListItem('我的', Icons.account_balance, '我的界面欢迎 方天画戟'),
  ]; // 定义数据
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          backgroundColor: Theme.of(context).colorScheme.inversePrimary,
          title: Text(widget.title),
        ),
        body: Column(
          children: [
            Container(
              padding: const EdgeInsets.all(20),
              child: const Text(
                '点击下面tab进行组件测试',
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
              ),
            ),
            // Tab组件区域 Expanded 填充主轴剩余空间
            Expanded(
              child: TabCustomView(
                tabs: _tabsList,
              ),
            ),
          ],
        ));
  }
}

Tab组件

知识点:

  1. ✅✅数据传递-父传子
  2. ✅tab组件及控制器的绑定及使用
  3. ✅✅有状态组件如何获取父组件传递过来的数据
  4. ✅组件拆分
  5. 如何定义接口,通过class构造函数给class设置初始值方法
import 'package:flutter/material.dart';

/// Tab主组件
class TabCustomView extends StatefulWidget {
  final List<ITabsListItem> tabs;    // 定义父传子·接口
  final Color? selectColor;
  final String? fontSize;
  final String? tabHeight;
  const TabCustomView(
      {super.key,
      required this.tabs,  // 添加required表示会父组件必传递的
      this.fontSize,
      this.tabHeight,
      this.selectColor});

  @override
  State<TabCustomView> createState() => _TabCustomViewState();
}

class _TabCustomViewState extends State<TabCustomView>
    with SingleTickerProviderStateMixin {
  // 需要带上动画控制
  late TabController _tabController;
  @override
  void initState() {
    super.initState();
    _tabController =
        TabController(length: widget.tabs.length, vsync: this); // 初始化tab控制器
  }

  @override
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }  // 销毁时候需要销毁控制器

  @override
  Widget build(BuildContext context) {
    return SizedBox(
        height: double.infinity, // 占比100%
        child: Column(
          children: [
            SizedBox(
              // 盒子
              width: double.infinity,
              child: TabBar(
                  splashFactory: NoSplash.splashFactory, // 取消tabs点击切换波纹效果
                  controller: _tabController, // 需要绑定控制器
                  indicatorColor: Theme.of(context).primaryColor,
                  tabs: widget.tabs.map((e) => TabIcon(item: e)).toList()),
            ),
            Divider(height: 1),
            Expanded(
                child: TabBarView(
                    controller: _tabController,
                    children:
                        widget.tabs.map((e) => TabsContent(item: e)).toList()))
          ],
        ));
  }
}

/// tabItem项组件
class TabIcon extends StatelessWidget {
  final ITabsListItem item;
  const TabIcon({super.key, required this.item});

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      child: Column(
        spacing: 6,
        children: [Icon(item.icon), Text(item.title)],
      ),
    );
  }
}

/// tabContent项组件(就是下面的组件)
class TabsContent extends StatelessWidget {
  final ITabsListItem item;
  const TabsContent({super.key, required this.item}); // 定义给父组件进行传输数据

  @override
  Widget build(BuildContext context) {
    return Container(
        alignment: Alignment.center,
        width: double.infinity,
        color: item.color,
        padding: EdgeInsets.only(top: 20), // 设置顶部内边距为20
        child: Column(
          children: [
            Icon(
              item.icon,
              size: 50, // 图标大小  不用width进行设置
            ),
            Text(
              item.title,
              style: TextStyle(fontSize: 40, fontWeight: FontWeight.w800),
            ),
            Text(
              item.content,
              style: TextStyle(fontSize: 40, fontWeight: FontWeight.w400),
            ),
          ],
        ));
  }
}

/// tabsList接口
class ITabsListItem {
  late String title;
  late IconData icon;
  late String content;
  late Color? color;

  ITabsListItem(String title, IconData icon, String content,
      {Color? color = const Color.fromARGB(255, 59, 202, 202)}) {
    // 定义接口并,部分参数设置可选值并进行初始化赋值 赋初始值参数需要加上花括号
    this.title = title;
    this.icon = icon;
    this.content = content;
    this.color = color;
  }
}
// 1. Expanded组件,占满主轴剩余空间
// 2. SizedBox组件,设置固定宽高
// 3. Container组件.设置宽高及背景颜色等
规整页面代码,删除调试代码
import 'package:flutter/material.dart';
import 'package:flutter_basic_comp/comment/component/tab_custom_view.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '方天画戟的Flutter 组件 Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Comp Code 组件 Demo'),
    );
  }
}

// 页面主组件
class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  // 定义tab数据
  final List<ITabsListItem> _tabsList = [
    ITabsListItem('首页', Icons.home, '首页界面', color: Colors.blue),
    ITabsListItem('发现', Icons.find_replace_sharp, '发现界面', color: Colors.purple),
    ITabsListItem('消息', Icons.message, '消息界面'),
    ITabsListItem('我的', Icons.account_balance, '我的界面欢迎 方天画戟'),
  ]; // 定义数据
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: null,
        body: Column(
          children: [
            // Tab组件区域 Expanded 填充主轴剩余空间
            Expanded(
              child: TabCustomView(
                tabs: _tabsList,
              ),
            ),
          ],
        ));
  }
}

TabCustomview
知识点:

  1. 通过排列顺序调整页面结构
  2. 如何设置tab选中颜色
  3. column组件 能够竖直排列组件
  4. expand默认占满主轴空间
    tips:
  5. ?? 空值合并 如果前面一个为null or undefine 就用?? 后面的值
    import ‘package:flutter/material.dart’;
/// Tab主组件
class TabCustomView extends StatefulWidget {
  final List<ITabsListItem> tabs;
  final Color? selectColor;
  final String? fontSize;
  final String? tabHeight;
  const TabCustomView(
      {super.key,
      required this.tabs,
      this.fontSize,
      this.tabHeight,
      this.selectColor});

  @override
  State<TabCustomView> createState() => _TabCustomViewState();
}

class _TabCustomViewState extends State<TabCustomView>
    with SingleTickerProviderStateMixin {
  // 需要带上动画控制
  late TabController _tabController;
  @override
  void initState() {
    super.initState();
    _tabController =
        TabController(length: widget.tabs.length, vsync: this); // 初始化tab控制器
  }

  @override
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
        height: double.infinity, // 占比100%
        child: Column(
          children: [
            // 设置内容区域 Expanded 填充主轴剩余空间
            Expanded(
                child: TabBarView(
                    controller: _tabController,
                    children:
                        widget.tabs.map((e) => TabsContent(item: e)).toList())), // 通过循环便捷创建组件
            Container(
              width: double.infinity,
              padding: EdgeInsets.only(
                  top: 10), // 设置padding(内边距)  all是全部设置,only是在某几个方向设置
              child: TabBar(
                  splashFactory: NoSplash.splashFactory, // 取消tabs点击切换波纹效果
                  labelColor: widget.selectColor ?? Colors.blue,  // 设置选中颜色
                  controller: _tabController, // 需要绑定控制器
                  indicatorColor: Colors.transparent, // 设置tab下面的指示条,可以改一改试一试
                  tabs: widget.tabs.map((e) => TabIcon(item: e)).toList()),
            ),
          ],
        ));
  }
}

/// tabItem项组件
class TabIcon extends StatelessWidget {
  final ITabsListItem item;
  const TabIcon({super.key, required this.item});

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      child: Column(
        spacing: 6,
        children: [Icon(item.icon), Text(item.title)],
      ),
    );
  }
}

/// TabContent项组件(就是下面的组件)
class TabsContent extends StatelessWidget {
  final ITabsListItem item;
  const TabsContent({super.key, required this.item}); // 定义给父组件进行传输数据

  @override
  Widget build(BuildContext context) {
    return Container(
        alignment: Alignment.center,
        width: double.infinity,
        color: item.color,
        padding: EdgeInsets.only(top: 100), // 设置顶部内边距为20
        child: Column(
          children: [
            Icon(
              item.icon,
              size: 50, // 图标大小  不用width进行设置
            ),
            Text(
              item.title,
              style: TextStyle(fontSize: 40, fontWeight: FontWeight.w800),
            ),
            Text(
              item.content,
              style: TextStyle(fontSize: 40, fontWeight: FontWeight.w400),
            ),
          ],
        ));
  }
}

/// tabsList接口
class ITabsListItem {
  late String title;
  late IconData icon;
  late String content;
  late Color? color;

  ITabsListItem(String title, IconData icon, String content,
      {Color? color = const Color.fromARGB(255, 59, 202, 202)}) {
    // 定义接口并,部分参数设置可选值并进行初始化赋值 赋初始值参数需要加上花括号
    this.title = title;
    this.icon = icon;
    this.content = content;
    this.color = color;
  }
}
// 1. Expanded组件,占满主轴剩余空间
// 2. SizedBox组件,设置固定宽高
// 3. Container组件.设置宽高及背景颜色等
TabCustomView再优化,组件知识点讲解

知识点:
有状态组件组成

  1. 由两个class组件,第一层继承StatefulWidget ,第二层class继承State
  2. 第一层:
  3. 可以通过构造函数进行传递数据
  4. 可以定义一些不可变的参数(不能被修改)
  5. 第二层:
  6. 变量再整个class的生命周期存在
  7. 不能有显著构造函数,因为State对象是Flutter框架创建的
  8. by the way Build方法里面创建变量
  9. 每次build方法被调用变量都会被重新创建
  10. 定义的变量只能再这个build方法里面被调用
  11. 适合场景为临时变量或者动态值

媒体查询安全区高度

  1. 什么是安全区:例如手机电量那一栏就是属于软件的安全区,软件要注意避让安全区以防内容被遮盖
  2. 如何查询安全区高度:通过Flutter的媒体查询进行,定义安全区变量要找build方法
  3. final saveArea = MediaQuery.of(context).padding;
    1. 顶部安全区域(刘海屏等)
  4. 底部安全区域(底部导航栏等)
  5. 左侧安全区域(左滑返回等)
  6. 右侧安全区域(右滑返回等)

基础组件

  1. Container:容器组件,能设置宽高背景色
  2. SizeBox:容器组件,只能设置宽高,只设置宽高情况下建议用
  3. Column:竖直方向排列组件,传入children数组能竖直顺序排列数组内的组件, btw 横向排列组件是Row

整体代码

import 'package:flutter/material.dart';

/// Tab主组件
class TabCustomView extends StatefulWidget {
  // 1. ✅第一层class通过构造函数传递参数
  // 2. 继承StatefulWidget
  // 3. 可以在这里配置参数-不变的
  // 4. 定义参数不可变(不可被修改)
  final List<ITabsListItem> tabs;
  final Color? selectColor;
  final String? fontSize;
  final String? tabHeight;
  // 构造函数,接受传递的值
  const TabCustomView(
      {super.key,
      required this.tabs,
      this.fontSize,
      this.tabHeight,
      this.selectColor});

  @override
  State<TabCustomView> createState() => _TabCustomViewState();
}

class _TabCustomViewState extends State<TabCustomView>
    with SingleTickerProviderStateMixin {
  // 第二层class参数
  // 1.变量在整个class生命周期存在
  // 2.变量能被修改

  // 第二层class通过widget.访问上面的StatefulWidget的参数
  // 1.不能有显著的构造函数,因为State对象是Flutter框架创建的
  // 需要带上动画控制
  late TabController _tabController;
  @override
  void initState() {
    super.initState();
    _tabController =
        TabController(length: widget.tabs.length, vsync: this); // 初始化tab控制器
  }

  @override
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // 1. 在build方法里面创建变量
    // 2. 每次build方法调用都需要创建变量
    // 3. 只能在这个build里面调用
    // 4. 适合临时计算或动态值

    // 通过媒体查询查询设备的安全区域(刘海屏等)
    // 1. 顶部安全区域(刘海屏等)
    // 2. 底部安全区域(底部导航栏等)
    // 3. 左侧安全区域(左滑返回等)
    // 4. 右侧安全区域(右滑返回等)
    final saveArea = MediaQuery.of(context).padding;

    return SizedBox(
        height: double.infinity, // 占比100%
        child: Column(
          children: [
            // 设置内容区域 Expanded 填充主轴剩余空间
            Expanded(
                child: TabBarView(
                    controller: _tabController,
                    children:
                        widget.tabs.map((e) => TabsContent(item: e)).toList())),
            Container(
              width: double.infinity,
              padding: EdgeInsets.only(top: 10, bottom: saveArea.bottom - 10),
              // 设置padding(内边距)  all是全部设置,only是在某几个方向设置
              // 底部安全区域-6 是为了适配底部导航栏
              child: TabBar(
                  splashFactory: NoSplash.splashFactory, // 取消tabs点击切换波纹效果
                  labelColor: widget.selectColor ?? Colors.blue, // 设置选中颜色
                  dividerHeight: 0,
                  controller: _tabController, // 需要绑定控制器
                  indicatorColor: Colors.transparent, // 设置tab下面的指示条,可以改一改试一试
                  tabs: widget.tabs.map((e) => TabIcon(item: e)).toList()),
            ),
          ],
        ));
  }
}

/// tabItem项组件
class TabIcon extends StatelessWidget {
  final ITabsListItem item;
  const TabIcon({super.key, required this.item});

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      child: Column(
        spacing: 6,
        children: [Icon(item.icon), Text(item.title)],
      ),
    );
  }
}

/// TabContent项组件(就是下面的组件)
class TabsContent extends StatelessWidget {
  final ITabsListItem item;
  const TabsContent({super.key, required this.item}); // 定义给父组件进行传输数据

  @override
  Widget build(BuildContext context) {
    return Container(
        alignment: Alignment.center,
        width: double.infinity,
        color: item.color,
        padding: EdgeInsets.only(top: 100), // 设置顶部内边距为20
        child: Column(
          children: [
            Icon(
              item.icon,
              size: 50, // 图标大小  不用width进行设置
            ),
            Text(
              item.title,
              style: TextStyle(fontSize: 40, fontWeight: FontWeight.w800),
            ),
            Text(
              item.content,
              style: TextStyle(fontSize: 40, fontWeight: FontWeight.w400),
            ),
          ],
        ));
  }
}

/// tabsList接口
class ITabsListItem {
  late String title;
  late IconData icon;
  late String content;
  late Color? color;

  ITabsListItem(String title, IconData icon, String content,
      {Color? color = const Color.fromARGB(255, 59, 202, 202)}) {
    // 定义接口,部分参数设置可选值并进行初始化赋值 赋初始值参数需要加上花括号
    this.title = title;
    this.icon = icon;
    this.content = content;
    this.color = color;
  }
}
// 1. Expanded组件,占满主轴剩余空间
// 2. SizedBox组件,设置固定宽高
// 3. Container组件.设置宽高及背景颜色等
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐