首页 > 其他分享 >前端学习-flutter学习-003-Widget 简介

前端学习-flutter学习-003-Widget 简介

时间:2024-07-07 11:53:34浏览次数:8  
标签:widget const key Text Widget 003 context override flutter

学习链接

什么是widget

描述UI元素的配置信息
Widget类本身是一个抽象类,其中最核心的就是定义了createElement()接口,在 Flutter 开发中,我们一般都不用直接继承Widget类来实现一个新组件,相反,我们通常会通过继承StatelessWidget或StatefulWidget来间接继承widget类来实现。StatelessWidget和StatefulWidget都是直接继承自Widget类

四棵树

根据 Widget 树生成一个 Element 树,Element 树中的节点都继承自 Element 类。
根据 Element 树生成 Render 树(渲染树),渲染树中的节点都继承自RenderObject 类。
根据渲染树生成 Layer 树,然后上屏显示,Layer 树中的节点都继承自 Layer 类。

StatelessWidget

根据学习资料的示例,自己试着调整写了一下 main.dart

import 'package:flutter/material.dart';

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

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

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'StatelessWidget'),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  Widget build(BuildContext context) {
    return Echo(text: "hello world");
  }

}


class Echo extends StatelessWidget  {
  const Echo({
    Key? key,
    required this.text,
    this.backgroundColor = Colors.grey, //默认为灰色
  }):super(key:key);

  final String text;
  final Color backgroundColor;

  @override
  Widget build(BuildContext context) {
    return
    Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text("StatelessWidget"),
      ),
      body: Center(
        child: Container(
          color: backgroundColor,
          child: Text(text),
        ),
      ),
    );
  }

}


build方法有一个context参数,它是BuildContext类的一个实例,表示当前 widget 在 widget 树中的上下文,它提供了从当前 widget 开始向上遍历 widget 树以及按照 widget 类型查找父级 widget 的方法
示例代码

import 'package:flutter/material.dart';

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

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

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const ContextRoute(),
    );
  }
}


class ContextRoute extends StatelessWidget  {
  const ContextRoute();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text("Context测试"),
      ),
      body: Container(
        child: Builder(builder: (context) {
          // 在 widget 树中向上查找最近的父级`Scaffold`  widget
          Scaffold scaffold = context.findAncestorWidgetOfExactType<Scaffold>() as Scaffold;
          // 直接返回 AppBar的title, 此处实际上是Text("Context测试")
          return (scaffold.appBar as AppBar).title!;
        }),
      ),
    );
  }
}

StatefulWidget

StatefulWidget也是继承自widget类,并重写了createElement()方法,不同的是返回的Element 对象并不相同;另外StatefulWidget类中添加了一个新的接口createState()。
类定义:

abstract class StatefulWidget extends Widget {
  const StatefulWidget({ Key key }) : super(key: key);
    
  @override
  StatefulElement createElement() => StatefulElement(this);
    
  @protected
  State createState();
}

StatefulElement 间接继承自Element类,与StatefulWidget相对应(作为其配置数据)。StatefulElement中可能会多次调用createState()来创建状态(State)对象。

createState() 用于创建和 StatefulWidget 相关的状态,它在StatefulWidget 的生命周期中可能会被多次调用。
State 对象和StatefulElement具有一一对应的关系

State

简介

  • 一个 StatefulWidget 类会对应一个 State 类,State表示与其对应的 StatefulWidget 要维护的状态
  • State 中的保存的状态信息可以
  • 当State被改变时,可以手动调用其setState()方法通知Flutter 框架状态发生改变,重新调用其build方法重新构建 widget 树,更新UI
  • State的两个常用属性:widget、context;widget是与该 State 实例关联的 widget 实例、context作用同StatelessWidget 的BuildContext

State的生命周期

import 'package:flutter/material.dart';
import 'dart:io';
import 'dart:developer' as developer;

void main() => runApp(const StateLifecycleTest());


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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const CounterWidget(),
    );
    // return CounterWidget();
  }
}

class CounterWidget extends StatefulWidget {
  const CounterWidget({Key? key, this.initValue = 0});

  final int initValue;

  @override
  _CounterWidgetState createState() => _CounterWidgetState();
}


class _CounterWidgetState extends State<CounterWidget> {
  int _counter = 0;

  @override
  void initState() {
    super.initState();
    //初始化状态
    _counter = widget.initValue;
    print("initState");
  }

  @override
  Widget build(BuildContext context) {
    print("build");
    return Scaffold(
      body: Center(
        child: TextButton(
          child: Text('$_counter'),
          //点击后计数器自增
          onPressed: () => setState(
                () => ++_counter,
          ),
        ),
      ),
    );
  }

  @override
  void didUpdateWidget(CounterWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("didUpdateWidget ");
  }

  @override
  void deactivate() {
    super.deactivate();
    print("deactivate");
  }

  @override
  void dispose() {
    super.dispose();
    print("dispose");
  }

  @override
  void reassemble() {
    super.reassemble();
    print("reassemble");
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print("didChangeDependencies");
  }
}


class ContextRoute extends StatelessWidget  {
  const ContextRoute();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text("Context测试"),
      ),
      body: Container(
        child: Builder(builder: (context) {
          // 在 widget 树中向上查找最近的父级`Scaffold`  widget
          Scaffold scaffold = context.findAncestorWidgetOfExactType<Scaffold>() as Scaffold;
          // 直接返回 AppBar的title, 此处实际上是Text("Context测试")
          return (scaffold.appBar as AppBar).title!;
        }),
      ),
    );
  }
}

运行以上代码,输出:

I/flutter ( 5053): initState
I/flutter ( 5053): didChangeDependencies
I/flutter ( 5053): build

点击热重载,输出

I/flutter ( 5053): reassemble
I/flutter ( 5053): build

在 widget 树中移除CounterWidget,home: const CounterWidget(),修改为home: const Text('hjklh')

I/flutter ( 5053): reassemble
I/flutter ( 5053): deactivate
I/flutter ( 5053): dispose

改回home: const CounterWidget(),

I/flutter ( 5053): initState
I/flutter ( 5053): didChangeDependencies
I/flutter ( 5053): build

总结:

  • widget第一次插入widget树时调用initState(),仅调用1次
  • 当State对象的依赖发生变化时调用didChangeDependencies(),组件第一次被创建后挂载的时候(包括重创建)对应的didChangeDependencies()也会被调用
  • 构建 widget 子树时调用build()
  • reassemble():此回调是专门为了开发调试而提供的,在热重载(hot reload)时会被调用,此回调在Release模式下永远不会被调用
  • didUpdateWidget ():在 widget 重新构建时,Flutter 框架会调用widget.canUpdate来检测 widget 树中同一位置的新旧节点,然后决定是否需要更新,如果widget.canUpdate返回true则会调用此回调。正如之前所述,widget.canUpdate会在新旧 widget 的 key 和 runtimeType 同时相等时会返回true,也就是说在在新旧 widget 的key和runtimeType同时相等时didUpdateWidget()就会被调用。
  • deactivate():当 State 对象从树中被移除、位置移动时,会调用此回调
  • dispose():当 State 对象从树中被永久移除时调用;通常在此回调中释放资源

在 widget 树中获取State对象

通过context获取

有几种写法:

// 1
// 查找父级最近的Scaffold对应的ScaffoldState对象
ScaffoldState _state = context.findAncestorStateOfType<ScaffoldState>()!;
// 2
// Scaffold也提供了一个of方法,我们其实是可以直接调用它的
ScaffoldState _state = Scaffold.of(context);
// 3
ScaffoldMessenger.of(context).showSnackBar(
  SnackBar(content: Text("我是SnackBar")),
);

整体的代码:

import 'package:flutter/material.dart';
import 'dart:io';
import 'dart:developer' as developer;
// void main() {
//   runApp(const MyApp());
// }

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

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const GetStateObjectRoute(),
    );
  }
}


class GetStateObjectRoute extends StatefulWidget {
  const GetStateObjectRoute({Key? key}) : super(key: key);

  @override
  State<GetStateObjectRoute> createState() => _GetStateObjectRouteState();
}

class _GetStateObjectRouteState extends State<GetStateObjectRoute> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("子树中获取State对象"),
      ),
      body: Center(
        child: Column(
          children: [
            Builder(builder: (context) {
              return ElevatedButton(
                onPressed: () {
                  // 查找父级最近的Scaffold对应的ScaffoldState对象
                  ScaffoldState _state = context.findAncestorStateOfType<ScaffoldState>()!;
                  // 打开抽屉菜单
                  _state.openDrawer();
                },
                child: Text('打开抽屉菜单1'),
              );
            }),
            Builder(builder: (context) {
              return ElevatedButton(
                onPressed: () {
                  // 查找父级最近的Scaffold对应的ScaffoldState对象
                  // 如果 StatefulWidget 的状态是希望暴露出的,应当在 StatefulWidget 中提供一个of 静态方法来获取其 State 对象,开发者便可直接通过该方法来获取;如果 State不希望暴露,则不提供of方法
                  // Scaffold也提供了一个of方法,我们其实是可以直接调用它的
                  ScaffoldState _state = Scaffold.of(context);
                  // 打开抽屉菜单
                  _state.openDrawer();
                },
                child: Text('打开抽屉菜单2'),
              );
            }),
            Builder(builder: (context) {
              return ElevatedButton(
                onPressed: () {
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(content: Text("我是SnackBar")),
                  );
                },
                child: Text('显示SnackBar'),
              );
            }),
          ],
        ),
      ),
      drawer: Drawer(),
    );
  }
}

通过GlobalKey获取

用GlobalKey改写了上面的代码
核心点在:

  • 定义一个静态变量 static GlobalKey _globalKey= GlobalKey();
  • 给Scaffold设置key为_globalKey
  • 使用_globalKey _globalKey.currentState.openDrawer()

但是改写SnackBar失败了,参考了这篇
但是还是没成功

import 'package:flutter/material.dart';
import 'dart:io';
import 'dart:developer' as developer;
// void main() {
//   runApp(const MyApp());
// }

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

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const GetStateObjectRoute(),
    );
  }
}


class GetStateObjectRoute extends StatefulWidget {
  const GetStateObjectRoute({Key? key}) : super(key: key);

  @override
  State<GetStateObjectRoute> createState() => _GetStateObjectRouteState();
}

class _GetStateObjectRouteState extends State<GetStateObjectRoute> {
  static GlobalKey<ScaffoldState> _globalKey= GlobalKey();
  static GlobalKey<ScaffoldMessengerState> _globalKey2= GlobalKey<ScaffoldMessengerState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _globalKey, //设置key
      appBar: AppBar(
        title: Text("子树中获取State对象"),
      ),
      body: Center(
        child: Column(
          children: [
            Builder(builder: (context) {
              return ElevatedButton(
                onPressed: () {
                  // // 查找父级最近的Scaffold对应的ScaffoldState对象
                  // ScaffoldState _state = context.findAncestorStateOfType<ScaffoldState>()!;
                  // // 打开抽屉菜单
                  // _state.openDrawer();
                  _globalKey.currentState?.openDrawer();
                },
                child: Text('打开抽屉菜单3'),
              );
            }),
            Builder(builder: (context) {
              return ElevatedButton(
                onPressed: () {
                  // 查找父级最近的Scaffold对应的ScaffoldState对象
                  _globalKey.currentState?.openDrawer();
                },
                child: Text('打开抽屉菜单4'),
              );
            }),
            Builder(builder: (context) {
              return ElevatedButton(
                onPressed: () {
                  // _globalKey2.currentState?.showSnackBar(
                  //   SnackBar(content: Text('我是SnackBar22')),
                  // );
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(content: Text('DDDDD')),
                  );
                },
                child: Text('显示SnackBar2'),
              );
            }),
          ],
        ),
      ),
      drawer: Drawer(),
    );
  }
}

通过 RenderObject 自定义 Widget

StatelessWidget 和 StatefulWidget 都是用于组合其他组件的, Text 、Column、Align等通过定义 RenderObject 来实现;也就是 StatelessWidget 和 StatefulWidget 将通过 RenderObject 实现的组件组合起来
复制了一个示例代码

class CustomWidget extends LeafRenderObjectWidget{
  @override
  RenderObject createRenderObject(BuildContext context) {
    // 创建 RenderObject
    return RenderCustomObject();
  }
  @override
  void updateRenderObject(BuildContext context, RenderCustomObject  renderObject) {
    // 更新 RenderObject
    super.updateRenderObject(context, renderObject);
  }
}

class RenderCustomObject extends RenderBox{

  @override
  void performLayout() {
    // 实现布局逻辑
  }

  @override
  void paint(PaintingContext context, Offset offset) {
    // 实现绘制
  }
}

如果组件不会包含子组件,则我们可以直接继承自 LeafRenderObjectWidget
如果自定义的 widget 可以包含子组件,则可以根据子组件的数量来选择继承SingleChildRenderObjectWidget 或 MultiChildRenderObjectWidget

Flutter SDK内置组件库介绍

flutter提供基础组件库、Material 风格( Android 默认的视觉风格)组件库和、Cupertino 风格(iOS视觉风格)组件库

import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';

基础组件:Text、Row、Column、Stack、Container
Material 组件:Scaffold、AppBar、TextButton
Cupertino 组件:

  import 'package:flutter/cupertino.dart';
  void main() => runApp(CupertinoApp(home: CupertinoTestRoute()));
  
  
  
  class CupertinoTestRoute extends StatelessWidget  {
    const CupertinoTestRoute({Key? key}) : super(key: key);
  
    @override
    Widget build(BuildContext context) {
      return CupertinoPageScaffold(
        navigationBar: const CupertinoNavigationBar(
          middle: Text("Cupertino Demo"),
          // middle: Text("Cupertino Demo", textDirection: TextDirection.ltr),
        ),
        child: Center(
          child: CupertinoButton(
              color: CupertinoColors.activeBlue,
              child: const Text("Press", textDirection: TextDirection.ltr),
              // child: const Text("Press", textDirection: TextDirection.ltr),
              onPressed: () {}
          ),
        ),
      );
    }
  }

标签:widget,const,key,Text,Widget,003,context,override,flutter
From: https://www.cnblogs.com/ayubene/p/18284834

相关文章

  • P1038 [NOIP2003 提高组] 神经网络
    讲解区下面分几部分再详解一下这道题1.读入+处理注意,因为这是一个拓扑的题所以我们拓展点的时候要借助队列那如何发挥队列的用处呢?由题意,只有最初状态为1的点才会往后传递我们完全可以在读入的时候就把上述点push进队列中楼上大佬也证明过了,阈值u(我的代码中是x)可以一开......
  • serverpod 基于dart 的flutter 下一代app 以及web server 开发框架
    serverpod按照官方的介绍是基于dart的flutter下一代app以及webserver开发框架包含的能力代码生成 可以辅助通过分析server端代码生成客户端代码,这样调用就比较方便了trpc就干了这类的事情世界一流的日志处理内置cache 内置cache可以减少数据库的开销简单方便使用......
  • 前端学习-flutter学习-002-计数器示例学习
    学习参考链接拆解代码学习Material是一种标准的移动端和web端的视觉设计语言,Flutter默认提供了一套丰富的Material风格的UI组件。//导入了MaterialUI组件库。import'package:flutter/material.dart';main函数为应用程序的入口。main函数中调用了runApp方法......
  • flutter状态管理 provider使用
    provider是flutter官方推荐的状态管理插件,是基于InheritedWidget实现的。下面我们来讲一个provider的使用方法。1.在pubspec.yaml文件中添加provider:^6.1.2开发文档:https://pub-web.flutter-io.cn/packages/provider可以查看使用方法和最新版本号。添加完成后,进行保......
  • 用PyQt5打造炫酷界面:深入解析pyqt5-custom-widgets
    在PyQt5中,使用自定义小部件可以为应用程序增添更多实用性和时尚感。pyqt5-custom-widgets是一个开源项目,提供了一系列有用且时尚的自定义小部件,如开关按钮、动画按钮等。本文将详细介绍pyqt5-custom-widgets的安装和使用方法。安装可以使用PIP进行安装(根据你的平台,可能是......
  • flutter 插件开发
    先引用一段官方的介绍说明:https://docs.flutter.cn/packages-and-plugins/developing-packagesPackage包含以下几种类别:纯Dart库(Dartpackages)用Dart编写的传统package,比如path。其中一些可能包含Flutter的特定功能,因此依赖于Flutter框架,其使用范围仅限于Flu......
  • Flutter——最详细(Badge)使用教程
    背景主要常用于组件叠加上圆点提示;使用场景,消息数量提示,消息红点提示属性作用backgroundColor红点背景色smallSize设置红点大小isLabelVisible是否显示offset设置红点位置alignment设置红点位置child设置底部组件代码块classBadgePageextendsStatelessWidget{......
  • flutter项目正式打包上线
    正式打包修改应用版本以及升级打包 ......
  • QTableWidget单元格设置控件居中以及背景颜色问题
    QTableWidget的单元格如果要显示其他控件,可以使用setCellWidget,但是控件没有居中,要想使控件居中,可以先创建一个QWidget对象,并设置布局器,然后将控件放到布局器中,并把这个QWidget对象放到单元格内,但是此时若要设置单元格的背景颜色,QTableWidget只能通过设置QTableWidgetItem的颜色来......
  • flutter 打开设置页面
    if(status.isPermanentlyDenied){//如果用户已永久拒绝了定位权限,您可以引导用户到应用设置页面以手动启用权限//如果用户已永久拒绝了定位权限,您可以引导用户到应用设置页面以手动启用权限//constintent=AndroidIntent(//action:'and......