Flutter 问题系列第 76 篇在 Flutter 中 Builder 组件的作用以及如何解决 Scaffold.of 找不到上下文问题的解决文案

Posted Allen Su

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter 问题系列第 76 篇在 Flutter 中 Builder 组件的作用以及如何解决 Scaffold.of 找不到上下文问题的解决文案相关的知识,希望对你有一定的参考价值。

这是【Flutter 问题系列第 76 篇】,如果觉得有用的话,欢迎关注专栏。

文章目录

一:问题描述

写这篇博客的初衷是因为前几天面试时,面试官问了一个这样的问题,很遗憾之前我用 Builder 组件只是用它在函数体内定义一些变量,处理逻辑,返回不同的组件。鉴于当时这个问题没有答好,所以研究了 Builder 组件后写下了这篇博客,下面是博客正文。

先来看一段代码,如下图所示

点击按钮后,会报如下图所示的错误。

主要报错信息是 Scaffold.of() called with a context that does not contain a Scaffold ,意思是使用不包含脚手架的上下文调用了 Scaffold.of() 。

为什么会报这个错误呢?这里先卖个关子,此时先按照报错内容中给出的第一种解决文案试一试,先给按钮组件外层套一个 Builder 组件试试。

二:解决方案一,使用 Builder 组件

给按钮组件外部套一个 Builder 组件,如下图所示

此时再点击按钮后不再报错了,也正常返回了结果。

问题是解决了,可只是套了一个 Builder 组件就正常了,这是为什么呢?

三:源码分析

我们先来看一下 Builder 类的源码,如下图所示

源码很简单,可以看到 Builder 是一个继承于 StatelessWidget 的组件,通过 BuildContext 对象来构建 Widget 对象。

有一个必传参数 builder,其定义如下图所示

可以看到,自身只是将当前的 context 对象作为回调传递了出去,然后让开发者根据回调中的 context 进行组件的构建。

原来使用了 Builder 组件后再调用 Scaffold.of() 方法,用的 context 是自己的,而不是根组件的了。

再来看下 of 方法的部分代码,如下图所示

findAncestorStateOfType 会根据传入的上下文,获取指定类型祖先的 State,如果找到则返回 ScaffoldState 的结果,反之就会拋出如上的异常信息。

重点来了,拿小本本记一下

  • 未加 Builder 组件前,调用 Scaffold.of(context) ,此时用的上下文是根组件的 context,而根组件的 context 是传入进来的,of 方法向上查找不到 Scaffold 的实例。
  • 加了 Builder 组件后,在 Builder 组件内部调用了 Scaffold.of(context),此时是根据 Builder 中的 context 向上查找最近的祖先,可以找到 Scaffold 的实例,所以没有再拋出异常。

说了这么多,简单来说其实就一句话,就是调用 context 处理相关逻辑时,是否可以找到对应组件的实例,找不到就拋出异常。

四:解决文案二,使用自定义组件

知道为什么使用 Builder 组件后异常消失了,那自定义一个小组件不就是一样的道理了。

如下图所示

当然,此时点击按钮不会拋出异常,也可以正常返回结果。

五:解决文案三,使用 GlobalKey

也可以使用 GlobalKey 的方式实现,如下图所示

根据全局 key 的当前状态 currentState 强转为 ScaffoldState 即可,也可以正常返回结果。

ok,关于 Flutter 中 Builder 组件的作用以及如何解决 Scaffold.of 找不到上下文的解决文案便说到这里。

你的问题得到解决了吗?欢迎在评论区留言。

赠人玫瑰,手有余香,如果觉得文章不错,希望可以给个一键三连,感谢。


结束语

Google 的 Flutter 越来越火,截止 2023年3月12日 GitHub 标星已达 151K,Flutter 毅然是一种趋势,所以作为前端开发者,没有理由不趁早去学习。

无论你是 Flutter 新手还是已经入门了,不妨先点个关注,后续我会将 Flutter 中的常用组件(含有源码分析、组件的用法及注意事项)以及可能遇到的问题写到 CSDN 博客中,希望自己学习的同时,也可以帮助更多的人。

动手编写你的第一个 Flutter 应用

我将带领大家尝试编写一个 Flutter 应用,感受一下 Flutter 开发的语法特点和运行效率。

Flutter 应用运行起来比 RN 流畅、编译快、热加载快,所以开发和调试的效率非常高。本文将着重给大家讲解下 Flutter 官方默认创建的应用,然后编写一个简单的 Flutter 尝鲜小应用。

本文将主要介绍:

  • 用 Flutter 创建一个默认应用
  • Flutter 默认应用的分析讲解
  • Flutter 编写一个小 Demo

用 Flutter 创建一个默认应用

本文的开发工具 IDE 用的是 Visual Studio Code,当然也可以使用 Android Studio 进行开发。关于用 Visual Studio Code 创建新的 Flutter 项目前面讲过,这里就不再重复讲解了。

默认新建的 Flutter 项目都是这个简单的实例,运行效果如下图所示:

Flutter 官方实例

点击 + 号 FloatingActionButton,中间的 Text Widget 进行累加更新数字统计计数。整体功能还是很简单的,主要涉及内容为控件点击事件、Text Widget 的显示、 setState(() {...}) 更新内容等。

那么接下来通过代码结合注释讲解方式来看下这个官方实例 main.dart 的实现流程:

import 'package:flutter/material.dart';
//main.dart为应用入口dart类,里面void main()方法为入口函数// 这里是lambda缩略写法,完整写法为下面这种:// void main(){// return runApp(MyApp());// }void main() => runApp(MyApp());
class MyApp extends StatelessWidget { // 这个Widget是应用的根布局,类似于页面容器 //构建搭建页面 @override Widget build(BuildContext context) { //入口页使用MaterialApp这个页面脚手架 //可以快速构建页面 //MaterialApp这个脚手架默认自带顶部ToolBar、路由、主题、国际化等等配置 return MaterialApp( title: 'Flutter Demo', theme: ThemeData( // 在这里我们可以配置应用全局主题,后面主题课程部分会详细讲解 // // 我们可以通过flutter run命令来运行程序,会看到蓝色状态栏和标题栏 // 通过 primarySwatch属性来配置状态栏和标题栏颜色 primarySwatch: Colors.green, ), // 设置启动页面Widget home: MyHomePage(title: 'Flutter Demo Home Page'), ); }}
// 继承StatefulWidget,有状态管理class MyHomePage extends StatefulWidget { // 这个是有参构造方法,用来传值的,这里我们不管 MyHomePage({Key key, this.title}) : super(key: key);
final String title;
//重写创建状态 @override _MyHomePageState createState() => _MyHomePageState();}
// 自定义创建状态管理,继承自State<T>class _MyHomePageState extends State<MyHomePage> { //声明变量临时存储次数 int _counter = 0; // 定义方法来累加次数 void _incrementCounter() { setState(() { //setState里用于刷新UI和绑定数据 _counter++; }); }
@override Widget build(BuildContext context) { // 这个方法每次调用 setState 都会调用 // // Flutter框架已经帮我们优化了这部分,所以当我们需要刷新状态的时候不用担心性能问题 // 这个用来构建页面具体布局,这里使用了Scaffold脚手架 // 里面包含了AppBar、body、bottomNavigationBar、floatingActionButton等 return Scaffold( appBar: AppBar( // 通过配置AppBar属性来控制显示效果,这里通过title来设置标题内容 title: Text(widget.title), ), body: Center( // body部分用Center Widget布局来加载Widget布局内容,子控件居中排列 child: Column( // Column是一个纵向列布局,子控件纵向排列 mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ //子控件,Text Widget用来显示文字内容 Text( 'You have pushed the button this many times:', ), // 动态绑定数据 Text( '$_counter', style: Theme.of(context).textTheme.display1, ), ], ), ), // 浮动+号按钮 floatingActionButton: FloatingActionButton( // 设置点击事件,执行_incrementCounter方法累加计数 onPressed: _incrementCounter, // 设置长按提示的信息 tooltip: 'Increment', // 设置图标 child: Icon(Icons.add), ), ); }}

通过 primarySwatch 属性来配置状态栏和标题栏颜色效果如下图:

动手编写你的第一个 Flutter 应用
Flutter 更改主题色调

一般入口文件用 MaterialApp 脚手架构建,其它页面可以不使用。

我们看下 MaterialApp 脚手架构造方法都提供了哪些可配置的属性功能:

const MaterialApp({ Key key, this.navigatorKey, this.home, this.routes = const <String, WidgetBuilder>{}, this.initialRoute, this.onGenerateRoute, this.onUnknownRoute, this.navigatorObservers = const <NavigatorObserver>[], this.builder, this.title = '', this.onGenerateTitle, this.color, this.theme, this.darkTheme, this.locale, this.localizationsDelegates, this.localeListResolutionCallback, this.localeResolutionCallback, this.supportedLocales = const <Locale>[Locale('en', 'US')], this.debugShowMaterialGrid = false, this.showPerformanceOverlay = false, this.checkerboardRasterCacheImages = false, this.checkerboardOffscreenLayers = false, this.showSemanticsDebugger = false, this.debugShowCheckedModeBanner = true, })

再看下 Scaffold 脚手架构造方法给我们提供的可配置的属性功能:

const Scaffold({ Key key, this.appBar, this.body, this.floatingActionButton, this.floatingActionButtonLocation, this.floatingActionButtonAnimator, this.persistentFooterButtons, this.drawer, this.endDrawer, this.bottomNavigationBar, this.bottomSheet, this.backgroundColor, this.resizeToAvoidBottomPadding, this.resizeToAvoidBottomInset, this.primary = true, this.drawerDragStartBehavior = DragStartBehavior.down, })

具体这些属性作用,大家可以大致有所了解,这里不再详细解释,后面课程会讲解。

看到这里,你觉得怎么样,Flutter 实现一个页面就是这样简单的。

动手编写你的第一个 Flutter 应用
文章摘自专栏

用 Flutter 编写一个小 Demo

接下来我们动手自己写一个简单的页面,实现页面显示一段文字加一张图片,点击按钮切换文字内容的小 Demo:

import 'package:flutter/material.dart';
void main() { return runApp(ShowApp());}
class ShowApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.green, ), home: ShowAppPage(), ); }}

class ShowAppPage extends StatefulWidget {
@override _ShowAppPageState createState() { return _ShowAppPageState(); }}
class _ShowAppPageState extends State<ShowAppPage> { String title = '春天的脚步近了,我们应该更加青春有朝气'; bool change = false;
void _changeTextContent() { setState(() { //setState里用于刷新UI和绑定数据 title = change ? "这个图片很好看,描述了春天的气息" : "春天的脚步近了,我们应该更加青春有朝气"; change = !change; }); }
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('春天的气息'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Padding( padding: EdgeInsets.all(10), child: Image.network( 'https://gss0.bdstatic.com/94o3dSag_xI4khGkpoWK1HF6hhy/baike/c0%3Dbaike272%2C5%2C5%2C272%2C90/sign=eaad8629b0096b63951456026d5aec21/342ac65c103853431b19c6279d13b07ecb8088e6.jpg'), ), // 动态绑定数据 Padding( padding: EdgeInsets.all(10), child: Text( '$title', style: Theme.of(context).textTheme.title, ), ),
RaisedButton( onPressed: _changeTextContent, child: Text('点击更换内容'), ), ], ), ), ); }}

用 flutter run 编译运行到真机或者模拟器上,运行效果如下图所示:

Flutter 小 Demo 效果

怎么样,效果是不是很好?构建这一个页面,对于其他语言可能要花费比较多的工作量,而 Flutter 构建的非常快,运行体验也很流畅。

总结

本文主要是给大家实践用 Flutter 搭建一个小应用 Demo,给大家一个入门的印象。俗话说熟能生巧,我们不但要理解理论知识,也需要动手实践,才能够更好地进行深入的研究和开发。建议如下:

  • 将本文内容动手敲一遍,亲身体验 Flutter 的应用编写和运行的流畅度。
  • 先了解 Flutter 的入口文件和入口函数,以及简单了解 MaterialApp、Scaffold 脚手架的概念。


以上是关于Flutter 问题系列第 76 篇在 Flutter 中 Builder 组件的作用以及如何解决 Scaffold.of 找不到上下文问题的解决文案的主要内容,如果未能解决你的问题,请参考以下文章

动手编写你的第一个 Flutter 应用

[Flutter] 写第一个 Flutter app,part1 要点

第11期Kotlin和Flutter,全都给你

Google官宣:Flutter全平台称霸

Flutter的安装与设置(第一节)

弃坑 React-Native,开启入坑 Flutter 之路