Flutter01-核心基础-Widget 简介

Widget 简介

StatelessWidget

StatelessWidget 用于不需要维护状态的场景,它通常在build方法中通过嵌套其他 widget 来构建UI,在构建过程中会递归的构建其嵌套的 widget 。

下面的代码,实现了一个回显字符串的Echo widget :

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 Center(
      child: Container(
        color: backgroundColor,
        child: Text(
          text,
          textDirection: TextDirection.ltr,
        ),
      ),
    );
  }
}

void main(List<String> args) {
  runApp(
    const Echo(text: "hello"),
  );
}

运行后效果如图:

image-20240215160724586

Context

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

下面是在子树中获取父级 widget 的一个示例:

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

  @override
  Widget build(BuildContext context) {
    Scaffold scaffold = context.findAncestorWidgetOfExactType<Scaffold>()!;
    return (scaffold.appBar as AppBar).title!;
  }
}

void main(List<String> args) {
  runApp(MaterialApp(
    title: "My App",
    home: Scaffold(
      appBar: AppBar(
        title: const Text("Context 测试"),
      ),
      body: const ContextRoute(),
    ),
  ));
}

运行后的效果如图:

image-20240215161540425

StatefulWidget

  • createState() 用于创建和 StatefulWidget 相关的状态,它在StatefulWidget 的生命周期中可能会被多次调用。

  • 例如,当一个 StatefulWidget 同时插入到 widget 树的多个位置时,Flutter 框架就会调用该方法为每一个位置生成一个独立的State实例

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

  final int initValue;

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

State

一个 StatefulWidget 类会对应一个 State 类,State表示与其对应的 StatefulWidget 要维护的状态,State 中的保存的状态信息可以:

  • 在 widget 构建时可以被同步读取。

  • 在 widget 生命周期中可以被改变,当State被改变时,可以手动调用其setState()方法通知Flutter 框架状态发生改变,Flutter 框架在收到消息后,会重新调用其build方法重新构建 widget 树,从而达到更新UI的目的。

State 中有两个常用属性:

  • widget,它表示与该 State 实例关联的 widget 实例,由Flutter 框架动态设置。如果 widget 被修改了,Flutter 框架会动态设置State. widget 为新的 widget 实例。

  • context。StatefulWidget对应的 BuildContext,作用同StatelessWidget 的BuildContext。

State生命周期

实现一个计数器 CounterWidget 组件 ,点击它可以使计数器加1。

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

  final int initValue;

  @override
  State<CounterWidget> 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 MaterialApp(
      title: "My App",
      home: 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");
  }
}

void main(List<String> args) {
  runApp(const CounterWidget());
}

StatefulWidget 生命周期如图:

image-20240215162707355

在 widget 树中获取State对象

由于 StatefulWidget 的的具体逻辑都在其 State 中,所以很多时候,我们需要获取 StatefulWidget 对应的State 对象来调用一些方法,比如Scaffold组件对应的状态类ScaffoldState中就定义了打开 SnackBar(路由页底部提示条)的方法。

  • 通过Context获取

context对象有一个findAncestorStateOfType()方法,该方法可以从当前节点沿着 widget 树向上查找指定类型的 StatefulWidget 对应的 State 对象。下面是实现打开 SnackBar 的示例:

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 MaterialApp(
      title: "My App",
      home: Scaffold(
        appBar: AppBar(
          title: const Text("子树中获取State对象"),
        ),
        body: Center(
          child: Column(
            children: [
              Builder(builder: (context) {
                return ElevatedButton(
                  onPressed: () {
                    // 查找父级最近的Scaffold对应的ScaffoldState对象
                    ScaffoldState _state =
                        context.findAncestorStateOfType<ScaffoldState>()!;
                    // 打开抽屉菜单
                    _state.openDrawer();
                  },
                  child: const Text('打开抽屉菜单1'),
                );
              }),
              Builder(builder: (context) {
                return ElevatedButton(
                  onPressed: () {
                    // 直接通过of静态方法来获取ScaffoldState
                    ScaffoldState _state = Scaffold.of(context);
                    // 打开抽屉菜单
                    _state.openDrawer();
                  },
                  child: const Text('打开抽屉菜单2'),
                );
              }),
              Builder(builder: (context) {
                return ElevatedButton(
                  onPressed: () {
                    ScaffoldMessenger.of(context).showSnackBar(
                      const SnackBar(content: Text("我是SnackBar")),
                    );
                  },
                  child: const Text('显示SnackBar'),
                );
              }),
            ],
          ),
        ),
        drawer: const Drawer(),
      ),
    );
  }
}

void main(List<String> args) {
  runApp(const GetStateObjectRoute());
}

上面示例运行后,效果如图所示:

image-20240215163503180
  • 通过GlobalKey
class GetStateObjectRoute extends StatefulWidget {
  const GetStateObjectRoute({Key? key}) : super(key: key);

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

class _GetStateObjectRouteState extends State<GetStateObjectRoute> {
  // 定义一个globalKey, 由于GlobalKey要保持全局唯一性,我们使用静态变量存储
  static final GlobalKey<ScaffoldState> _globalKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "My App",
      home: Scaffold(
        key: _globalKey, // 设置key
        appBar: AppBar(
          title: const Text("子树中获取State对象"),
        ),
        body: Center(
          child: Column(
            children: [
              Builder(builder: (context) {
                return ElevatedButton(
                  onPressed: () {
                    // 通过GlobalKey来获取State对象
                    (_globalKey.currentState as ScaffoldState).openDrawer();
                  },
                  child: const Text('打开抽屉菜单1'),
                );
              }),
            ],
          ),
        ),
        drawer: const Drawer(),
      ),
    );
  }
}

void main(List<String> args) {
  runApp(const GetStateObjectRoute());
}