一 面试题汇总
- Widget在一些窄屏设备上,文本溢出了,你会如何修复呐?
- Row显示宽度太窄无法容纳它们时,子节点自动换行到下一行展示如何操作
- 如何将cheese变成私有变量,怎样将它变成全局变量,什么时候你使用全局变量?
- hot reload和hot restart的区别是什么?
- StatelessWidget和StatefulWidget的区别是什么?
- WidgetsApp和MaterialApp的区别什么?
- 可以嵌套使用Scaffold吗,为什么或者为什么不?
- 什么时候适合使用packages、plugins或者三方库?
- 怎么减少Widget的重新构建?
- 什么是BuildContext,它有什么用?
- 在Flutter应用程序中,你怎么和native进行交互?
- 你可以做哪种类型的测试?
- 不同状态管理框架的优缺点是什么?
二 面试题解答(仅供参考)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| 在 Flutter 中,当 Widget 在窄屏设备上出现文本溢出问题时,可以采取以下几种方法来修复
1. 使用 TextOverflow 属性 -Text 组件提供了 overflow 属性,可以控制文本溢出时的显示方式。 -常用的值包括 TextOverflow.clip:截断超出部分。 TextOverflow.fade:淡出超出部分。 TextOverflow.ellipsis:在末尾显示省略号。
2.使用 Expanded 或 Flexible 组件 -将 Text 组件放置在 Expanded 或 Flexible 组件中,使其可以根据可用空间进行伸缩。 -这可以确保文本在可用空间内显示,并自动换行。
3.使用 SingleChildScrollView 组件 -如果文本内容较长,可以使用 SingleChildScrollView 组件,使其可以滚动。 -这允许用户滚动查看所有文本内容
4.使用 Wrap 组件 -如果文本内容需要换行显示,可以使用 Wrap 组件。 -Wrap 组件会自动将超出水平空间的文本换行显示
5.使用 MediaQuery 获取屏幕宽度 -使用 MediaQuery.of(context).size.width 获取屏幕宽度。 -根据屏幕宽度动态调整文本的样式,例如字体大小或最大行数。
6.使用 FractionallySizedBox 组件 -使用 FractionallySizedBox 组件,设置文本 Widget 的宽度为屏幕宽度的百分比。 -这可以确保文本 Widget 的宽度适应不同屏幕尺寸。
7.使用 AutoSizeText 组件 使用 auto_size_text 插件,它可以自动调整文本的字体大小,以适应可用空间
8.避免使用固定宽度 尽量避免为文本 Widget 设置固定宽度,而是使用相对宽度或自适应宽度。
9.使用约束布局 使用 ConstrainedBox 组件,为文本 Widget 设置尺寸限制。
|
2.2 Row显示宽度太窄无法容纳它们时,子节点自动换行到下一行展示如何操作
2.3 如何将cheese变成私有变量,怎样将它变成全局变量,什么时候你使用全局变量?
1 2 3 4 5 6 7 8 9 10 11 12
| 给定下面类 class Pizza { String cheese = 'cheddar'; }
1-在变量的前面添加下划线_,可以使它在库中私有化 class Pizza { String _cheese = 'cheddar'; }
2-想要一个全局变量,只需要将变量移到类的外面就可以了。 String cheese = 'cheddar';
|
2.4 hot reload和hot restart的区别是什么?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 在 Flutter 中,Hot Reload 和 Hot Restart 都是用来加速开发的工具,但它们的作用和机制不同:
1. Hot Reload(热重载) -作用: 更新 UI 界面,保留当前状态。 -机制: 只重新加载修改的代码片段,更新 Widget 树,不重建整个应用。 -使用场景: 调整 UI、修复逻辑、微调布局等。 -优点: 快速预览效果,保持页面状态。 -限制: 无法更新 main() 方法、initState()、全局变量等。
2. Hot Restart(热重启) -作用: 重启整个应用,状态会被重置。 -机制: 重建整个 Widget 树,重新执行 main() 方法,清空内存和状态。 -使用场景: 需要全局重置、修改初始化逻辑、调整全局变量时。 -优点: 确保应用从零开始运行,避免缓存影响。 -缺点: 相比 Hot Reload 较慢,页面状态会丢失。
3.总结: -Hot Reload:快、保状态,适合调 UI 和逻辑。 -Hot Restart:慢、重置状态,适合改全局配置和初始化逻辑。
开发时优先用 Hot Reload,必要时用 Hot Restart
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 在 Flutter 中,StatelessWidget和StatefulWidget是两种常见的Widget类型,它们的核心区别在于是否需要维护状态:
1.StatelessWidget(无状态组件) -特点:不可变,状态固定,一旦创建后就不会改变。 -用途:展示静态 UI,比如文本、图标、按钮等。 -什么时候用:UI 内容固定,或者依赖外部传入的数据而不会自行变化时。
2.StatefulWidget(有状态组件) -特点:可变,包含一个 State 对象来保存状态,状态改变时可以重建 UI。 -用途:需要动态交互的 UI,比如表单、动画、倒计时等。 -什么时候用:UI 需要根据用户操作、网络请求等动态更新时。
3.总结: -StatelessWidget:静态、无状态,UI 不会随事件变化而更新。 -StatefulWidget:动态、有状态,通过 setState() 触发 UI 重建。
如果页面不需要更新,用 StatelessWidget 更轻量;若需要交互或动态更新,用 StatefulWidget!
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| 在 Flutter 中,WidgetsApp 和 MaterialApp 都是应用的根 Widget,它们的核心区别在于提供的功能和设计风格:
1.WidgetsApp -特点:提供最基本的应用结构,是更底层的组件。 -用途:适合自定义 UI,或者不需要 Material Design 风格的场景。 -功能:路由管理、导航、应用生命周期监听、绑定 onGenerateRoute 等。 -适用场景:构建极简应用、自定义设计系统、游戏开发等。 -示例 WidgetsApp( color: Colors.blue, builder: (context, _) => Text('Hello WidgetsApp'), )
2.MaterialApp -特点:继承自 WidgetsApp,提供了 Material Design 风格的 UI 组件和行为。 -用途:快速构建符合 Material Design 规范的应用。 -功能:包含 WidgetsApp 的所有功能,额外提供主题、 Scaffold、AppBar、Snackbar、FloatingActionButton 等 Material 组件。 -适用场景:一般的移动端应用开发,符合 Google 的 Material Design 规范 -示例 MaterialApp( theme: ThemeData(primarySwatch: Colors.blue), home: Scaffold( appBar: AppBar(title: Text('Hello MaterialApp')), body: Center(child: Text('Hello')), ), )
3.总结: -WidgetsApp:底层、轻量,适合自定义 UI。 -MaterialApp:封装了 Material Design 风格,开发更方便。 -开发时:一般直接用 MaterialApp,除非有特殊设计需求!
|
2.7 可以嵌套使用Scaffold吗,为什么或者为什么不?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 在 Flutter 中,不建议嵌套使用 Scaffold,因为它可能导致一些 UI 行为异常,主要原因如下:
1.Scaffold 的作用: -Scaffold 是页面的结构基础,提供 AppBar、Body、FloatingActionButton、Drawer 等布局组件。 -每个页面通常只需要一个 Scaffold 作为根部结构,嵌套多个 Scaffold 会导致布局混乱。
2.嵌套问题: -AppBar 重叠:多个 AppBar 会层层堆叠,影响导航和返回按钮的行为。 -SnackBar、Drawer 无效:Scaffold.of(context) 查找最近的 Scaffold, 嵌套时会找错目标,导致 SnackBar、Drawer 等功能失效。 -页面结构混乱:内层 Scaffold 的 Body、FloatingActionButton 等不会与外层协作,容易破坏页面逻辑
3.替代方案: 如果需要类似 Scaffold 的布局结构,可以用NestedScrollView、Column 或 CustomScrollView代替, 确保页面只有一个 Scaffold
4.总结: -不要嵌套 Scaffold,会导致 AppBar、SnackBar、Drawer 等行为异常。 -正确做法:用 Column、Container、NestedScrollView 等替代,保持页面只有一个 Scaffold!
|
2.8 什么时候适合使用packages、plugins或者三方库?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| 在 Flutter 开发中,Packages、Plugins、第三方库能帮助提升开发效率,具体使用场景如下:
1.Packages(纯 Dart 包) -适用场景:需要纯 Dart 逻辑、跨平台的工具或功能时使用。 -特点:不依赖原生平台代码,完全用 Dart 编写,跨平台通用。 -示例:数据处理(intl 格式化日期)、状态管理(provider)、网络请求(http)等。 示例用法:import 'package:http/http.dart' as http;
2. Plugins(插件) -适用场景:需要调用原生平台功能(如摄像头、蓝牙、传感器)时使用。 -特点: 包含原生平台代码(Android 的 Kotlin/Java、iOS 的 Swift/Objective-C), 通过平台通道与 Dart 代码通信。 -示例:访问硬件设备(camera)、定位(geolocator)、权限管理(permission_handler)等。 -示例用法:import 'package:geolocator/geolocator.dart';
3.第三方库 -适用场景:遇到通用功能已有成熟方案时,避免重复造轮子。 -选择标准:看 GitHub Star 数、维护频率、问题反馈等指标,选靠谱的库。 -常用库: --UI 库:flutter_svg、lottie --状态管理:provider、bloc --网络请求:dio --本地存储:shared_preferences、hive
4.总结: -Packages:纯 Dart 逻辑、跨平台功能。 -Plugins:需要调用原生平台功能。 -第三方库:已有成熟方案时直接用,省时省
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| 在 Flutter 中,减少 Widget 的重新构建是提升应用性能的一个关键因素。 频繁的 Widget 重新构建会导致性能下降,特别是在复杂的 UI 或嵌套较深的场景下。 下面是一些减少 Widget 重新构建的策略:
1. 使用 const 构造函数 -适用场景:当 Widget 的内容不依赖于状态变化时,使用 const 修饰符。 -效果:使用 const 构造函数的 Widget 只会创建一次,之后不会被重新构建。 -示例:const Text('Hello, World!');
2.使用 Key 控制 Widget 更新 -适用场景: 在列表或动态变化的界面中,使用 Key 来标识每个 Widget 的唯一性,帮助 Flutter 高效更新 Widget 树。 -效果:通过 Key,Flutter 可以更精确地识别需要更新的部分,避免重新构建整个组件。 -示例 ListView( children: [ Container(key: ValueKey('item1')), Container(key: ValueKey('item2')), ], )
3.使用 shouldRebuild 来优化 CustomPainter 和 ListView -适用场景:对于自定义绘制的 Widget(如 CustomPainter)或者ListView、GridView等滚动组件。 -效果:通过 shouldRebuild 方法来判断是否需要重新绘制 Widget。 -示例 class MyCustomPainter extends CustomPainter { @override bool shouldRepaint(CustomPainter oldDelegate) { return false; // 如果不需要重绘,返回 false } }
4.使用 setState 仅更新需要更新的部分 -适用场景:只更新 UI 的小部分时,尽量将 setState 作用范围限定在变化的部分。 -效果:避免 setState 触发整个页面或大范围 Widget 的重建。 -示例 setState(() { // 只更新必要的状态 _counter++; });
5. 使用 Provider 或 InheritedWidget 提供高效的状态管理 -适用场景:使用状态管理工具(如 Provider)将数据放在全局状态中,只在需要的地方更新 Widget。 -效果:避免过度依赖 setState,减少不必要的 Widget 更新。 -示例 Provider( create: (_) => MyModel(), child: MyWidget(), )
6.使用 ListView.builder 或 GridView.builder -适用场景:列表中有大量数据时,使用 ListView.builder 或 GridView.builder 来按需构建列表项。 -效果:仅渲染当前可见的列表项,减少不必要的构建。 -示例 ListView.builder( itemCount: 100, itemBuilder: (context, index) { return ListTile(title: Text('Item $index')); }, )
7.使用 RepaintBoundary 分离重绘区域 -适用场景:需要高效渲染复杂界面时,使用 RepaintBoundary 来分离需要重绘的区域。 -效果:减少不必要的部分重绘,提升性能。 -示例 RepaintBoundary( child: YourWidget(), )
8.总结: -const 构造函数:避免 Widget 的重复构建。 -使用 Key:精确控制 Widget 更新。 -避免大范围 setState:只更新需要的部分。 -状态管理:通过高效的状态管理工具减少不必要的构建。 -按需构建:使用 ListView.builder 等按需加载。
|
2.10 什么是BuildContext,它有什么用?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| 在 Flutter 中,BuildContext 是一个非常重要的概念, 它代表了 Widget 树中的位置和上下文,并且提供了对当前 Widget 的父级、祖先 Widget 的访问权限。 它是许多 Flutter API 中的一个参数。
1. BuildContext 的作用: -代表位置和上下文:BuildContext 是Widget在Widget树中的位置,能够让我们访问该位置的父级 Widget。 -提供对祖先 Widget 的访问:通过 BuildContext,可以获取到父级或祖先 Widget 的状态、主题、路由等信息。 -导航和主题获取:常用来访问 Navigator、Theme 或 InheritedWidget 等全局状态或数据。
2. 常见用途: 2.1 访问父级或祖先 Widget: 通过BuildContext,你可以使用context.dependOnInheritedWidgetOfExactType<T>() 等方法访问祖先 Widget,常见于访问主题、路由等全局数据。 ThemeData theme = Theme.of(context);
2.2 导航(Navigation): BuildContext 是进行页面导航的核心,通常使用 Navigator 来控制页面跳转。 Navigator.push( context, MaterialPageRoute(builder: (context) => NextPage()), );
2.3 获取Scaffold或其他Widget的状态:使用Scaffold.of(context) 获取当前Scaffold的状态 Scaffold.of(context).openDrawer();
2.4 触发showDialog等操作:BuildContext也是弹出对话框、SnackBar等UI组件时需要的上下文。 showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: Text('Alert'), content: Text('This is a dialog'), ); }, );
3.总结: -BuildContext是Widget树中每个Widget的位置和上下文,帮助我们访问当前Widget的父级、祖先以及全局数据。 -主要用于 访问主题、导航、父级 Widget 状态,以及 显示对话框、SnackBar 等操作。 -使用场景:在需要访问祖先 Widget 或进行 UI 操作时,BuildContext 是必不可少的。
|
2.11 在Flutter应用程序中,你怎么和native进行交互?
1 2 3 4 5 6 7 8 9 10 11 12 13
| 通常你不需要和原生进行交互,因为Flutter或三方插件会处理这些问题, 但是,如果你发现确实有特殊需要访问一些底层平台,你可以使用平台channel。
其中一种类型是method channel,数据在Dart侧进行序列化,然后会将数据发送到原生侧, 你可以在原生侧编写代码响应交互,然后回传序列化后的数据。 在Android侧可以选用Kotlin或者Java,在iOS侧可以使用Objective-C或者Swift进行编写。
但是,在开发web的时候,你不需要使用channel,这时非必要的步骤。
第二种channel类型是event channel,你可以用来从native发送stream数据到flutter侧, 这对监控传感器数据的场景很有用。
可以在Flutter的文档platform channels中看到更详细的介绍
|
2.12 你可以做哪种类型的测试?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 在 Flutter 中,测试是确保应用质量和稳定性的重要手段。 Flutter 提供了多种类型的测试,包括单元测试、集成测试和 UI 测试。 下面是常见的几种测试类型:
1.单元测试(Unit Test) -目的:测试单个函数、方法或类的行为,确保它们按预期工作。通常是针对业务逻辑的测试。 -特点:单元测试不依赖于外部资源(如网络、数据库),通常运行快速。 -工具:Flutter 提供了 test 包用于编写单元测试
2.Widget 测试(Widget Test) -目的:测试 Flutter Widget 的行为,确保它们在不同状态下正确渲染和响应用户操作。 可以模拟 UI 交互(例如点击按钮、输入文本)并验证结果。 -特点:Widget 测试主要关注 UI 层,通常运行比单元测试慢一些,但可以测试交互逻辑。 -工具:使用 flutter_test 包进行 Widget 测试。
3.集成测试(Integration Test) -目的:测试整个应用或部分应用的集成行为,模拟用户操作并测试多组件交互。 这种测试通常是端到端的测试,用来验证应用的整体功能。 -特点:集成测试通常运行较慢,因为它涉及整个应用或较大部分的交互,常用于验证 UI 流程和用户交互。 -工具:使用 integration_test 包进行集成测试
4.总结: -单元测试:验证单个函数、方法或类的行为。 -Widget 测试:验证 Flutter UI 组件的正确渲染和交互。 -集成测试:验证整个应用或多个组件的协作与交互,通常为端到端测试
|
2.13 不同状态管理框架的优缺点是什么?
在 Flutter 中,有多种 状态管理框架,每种框架有不同的设计理念和使用场景。
下面是一些常见的状态管理框架的 优缺点:
1-setState(内置的状态管理)
1 2 3 4 5 6 7 8 9 10 11
| setState 是 Flutter 提供的最基本的状态管理方法,适用于简单的应用或局部更新。
优点: -简单易用:使用内置的 setState,无需引入外部库,适合小型应用或简单场景。 -不依赖外部库:Flutter 自带,无需安装和配置。 -直接反映 UI 更新:每次调用 setState,Flutter 会自动重新构建 UI。
缺点: -无法跨 Widget 树共享状态:只能在当前 Widget 内部更新状态,无法在多个 Widget 之间共享。 -状态管理混乱:在复杂应用中,多个 setState 会导致代码难以维护,状态逻辑可能分散在多个地方。 -性能问题:每次调用 setState 都会导致整个 Widget 的重建,可能引起性能下降。
|
2-Provider
1 2 3 4 5 6 7 8 9 10 11
| Provider 是 Flutter 中非常流行的状态管理库,基于 InheritedWidget 实现,适用于中到大型应用。
优点: -高效的状态共享:可以方便地在 Widget 树的多个层级之间共享状态。 -易于维护和扩展:状态逻辑与 UI 解耦,使得应用更易于管理和扩展。 -性能优化:只有依赖于状态的 Widget 会重新构建,避免了不必要的重建。 -社区支持:活跃的社区,丰富的文档和资源。
缺点: -学习曲线:对于初学者来说,理解 Provider 和 ChangeNotifier 的概念可能需要一些时间。 -多层嵌套:在较复杂的场景下,可能会导致 Widget 树的嵌套层数增多,影响可读性。
|
3-Riverpod
1 2 3 4 5 6 7 8 9 10 11 12
| Riverpod 是由 Provider 的作者创建的状态管理库, 它提供了更强大的功能和更好的可组合性,旨在解决 Provider 的一些限制。
优点: -完全解耦:Riverpod 不依赖于 Widget 树,状态管理与 UI 逻辑完全分离。 -更强的类型安全:Riverpod 提供了更强的类型检查,减少了错误的发生。 -优化性能:比 Provider 更加高效,避免了不必要的重建。 -更灵活的组合:支持在多个作用域和不同组件中管理状态。
缺点: -相对复杂:对于初学者来说,Riverpod 的学习曲线较陡,需要了解更多的概念和 API。 -不常用的功能过于复杂:一些高级特性(例如 ScopedReader)可能对于小型应用来说过于复杂。
|
4-BLoC(Business Logic Component)
1 2 3 4 5 6 7 8 9 10 11 12
| BLoC 是一种基于流(Streams)和响应式编程(Reactive Programming)的状态管理方法, 适用于大型应用,特别是需要复杂业务逻辑的场景。
优点: -解耦 UI 和业务逻辑:BLoC 强调 UI 层与业务逻辑层的分离,便于测试和维护。 -可扩展性强:适用于复杂和中大型应用,特别是在需要复杂业务逻辑时。 -流的管理:通过 Streams 管理应用状态,具有强大的异步处理能力。
缺点: -学习曲线陡峭:对于初学者来说,理解 Stream、Sink、StreamController 等概念需要一定时间。 -代码量较大:BLoC 需要编写大量的样板代码,可能导致开发效率降低。 -不适合小型应用:对于简单应用来说,BLoC 可能显得过于复杂和笨重。
|
5-Redux
1 2 3 4 5 6 7 8 9 10 11
| Redux 是一种基于单一状态树的状态管理库,灵感来自 JavaScript 的 Redux。适用于需要全局状态管理的应用。
优点: -全局状态管理:Redux 的状态是全局的,适用于大型应用中的复杂状态管理。 -一致性:通过 Actions 和 Reducers 管理状态变化,确保状态管理的一致性。 -易于调试:由于所有状态变化都在 Store 中有记录,易于追踪和调试。
缺点: -学习曲线陡峭:Redux 的概念较复杂,尤其是对于新手来说,理解 Store、Action、Reducer 等概念需要时间。 -代码样板多:需要大量的样板代码,如创建 Actions、Reducers 等,开发效率较低。 -性能问题:虽然通过中间件可以优化性能,但复杂的状态更新仍可能影响性能。
|
6-GetX
1 2 3 4 5 6 7 8 9 10
| GetX 是一个轻量级的状态管理框架,提供了高效、简洁的状态管理、路由和依赖注入。
优点: -轻量高效:GetX 提供非常简单的 API,易于上手,且性能优秀。 -一体化功能:不仅提供状态管理,还包括路由和依赖注入,适用于小到中型应用。 -极简代码:通过简洁的语法减少样板代码,开发效率高。
缺点: -不适合大型应用:GetX 的简洁性可能导致应用变得难以扩展,特别是在涉及复杂业务逻辑时。 -社区资源有限:相比其他框架,GetX 的社区支持和文档较少。
|
7-表格
框架 |
优点 |
缺点 |
setState |
简单易用、内置支持 |
难以跨 Widget 树共享、性能差 |
Provider |
高效的状态共享、易维护、社区支持 |
高效的状态共享、易维护、社区支持 |
Riverpod |
Riverpod |
相对复杂、高级特性过于复杂 |
BLoC |
解耦 UI 和业务逻辑、适合大型应用 |
学习曲线陡峭、代码量大 |
Redux |
全局状态、一致性、易调试 |
学习曲线、代码样板多、性能问题 |
GetX |
轻量高效、一体化功能、易用 |
不适合大型应用、社区支持少 |
8-如何选择
1 2 3
| 选择合适的状态管理框架要根据应用的规模、复杂度以及开发团队的熟悉程度来决定。 对于简单应用,setState 或 GetX 可能足够; 而对于大型应用,Provider、BLoC 或 Redux 会更合适。
|
三 参考