Flutter开发者必备面试问题与答案02
视频
https://www.bilibili.com/video/BV1Zk2dYyEBr/
前言
本文是 flutter 面试问题的第二讲,高频问答 10 题。
正文
11. PageRoute 是什么?
在 Flutter 中,PageRoute
是一个用于管理应用中页面导航的抽象类。它定义了如何在不同的页面之间进行切换,并提供了一些控制页面行为的功能。
主要特点
- 页面切换:
PageRoute
负责在不同页面(或屏幕)之间进行导航。它管理了页面的堆栈,使得用户可以前往新页面或返回到之前的页面。 - 动画效果:
PageRoute
可以定义页面切换时的动画效果。Flutter 提供了一些内置的路由实现,比如:MaterialPageRoute
:用于 Material Design 风格的应用,提供从底部向上推入页面的动画。CupertinoPageRoute
:用于 iOS 风格的应用,提供从右向左推入页面的动画。
- 生命周期管理:
PageRoute
提供了一些生命周期方法,如didChangeDependencies
和dispose
,用于在页面进入和退出时执行特定操作。
使用示例
下面是一个使用 MaterialPageRoute
的简单示例:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Home Page')),
body: Center(
child: ElevatedButton(
child: Text('Go to Second Page'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondPage()),
);
},
),
),
);
}
}
class SecondPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Second Page')),
body: Center(
child: Text('Welcome to the Second Page!'),
),
);
}
}
自定义 PageRoute
你可以创建自定义的 PageRoute
来实现特定的导航效果。例如,以下是一个简单的自定义路由实现:
class CustomPageRoute extends PageRouteBuilder {
final Widget page;
CustomPageRoute({required this.page})
: super(
pageBuilder: (context, animation, secondaryAnimation) => page,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
const begin = Offset(1.0, 0.0);
const end = Offset.zero;
const curve = Curves.easeInOut;
var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
var offsetAnimation = animation.drive(tween);
return SlideTransition(
position: offsetAnimation,
child: child,
);
},
);
}
12. 解释 async , await 和 Future ?
在 Flutter 和 Dart 中,async
、await
和 Future
是处理异步编程的关键概念。它们可以帮助你编写非阻塞的代码,使得应用能够在执行长时间运行的任务时保持响应。以下是对这三个概念的详细解释:
1. Future
- 定义:
Future
是一个表示可能在未来某个时间点完成的异步操作的对象。它可以用于表示一个延迟的值,通常用于处理异步任务的结果。 - 状态:
- 未完成(Pending):
Future
仍在进行中。 - 已完成(Completed):
Future
执行完毕,可以获取结果。 - 已失败(Error):
Future
执行过程中出现错误。
- 未完成(Pending):
- 使用示例:
Future<String> fetchData() async { // 模拟网络请求 await Future.delayed(Duration(seconds: 2)); return 'Data fetched'; }
2. async
- 定义:
async
是一个修饰符,用于声明一个异步函数。使用async
修饰的函数会返回一个Future
,即使函数内部没有显式地返回Future
。 - 特点:
- 在
async
函数中,你可以使用await
关键字等待一个Future
完成。 async
函数会自动将返回值包装在一个Future
中。
- 在
- 使用示例:
Future<void> loadData() async { String data = await fetchData(); print(data); // 输出 'Data fetched' }
3. await
- 定义:
await
是一个关键字,用于在async
函数中等待一个Future
完成,并返回其结果。 - 特点:
await
只能在async
函数中使用。- 使用
await
会暂停async
函数的执行,直到Future
完成,并返回结果。 await
让异步代码看起来像同步代码,从而提高代码的可读性。
- 使用示例:
Future<void> main() async { print('Fetching data...'); await loadData(); // 等待 loadData 完成 print('Data loaded'); }
整体示例
下面是一个完整的示例,展示了如何使用 async
、await
和 Future
:
import 'dart:async';
Future<String> fetchData() async {
// 模拟网络请求
await Future.delayed(Duration(seconds: 2));
return 'Data fetched';
}
Future<void> loadData() async {
String data = await fetchData();
print(data);
}
Future<void> main() async {
print('Fetching data...');
await loadData(); // 等待 loadData 完成
print('Data loaded');
}
- Future:表示一个异步操作的结果,可以是未完成、已完成或已失败。
- async:用于声明异步函数,返回一个
Future
。 - await:在
async
函数中等待一个Future
完成,并获取其结果。
通过使用 async
、await
和 Future
,你可以轻松地处理异步操作,使代码更加简洁和可读。
13. 你如何动态更新列表视图?
在 Flutter 中,动态更新列表视图通常使用 ListView
组件结合状态管理来实现。以下是几种常见的方法来动态更新列表视图:
1. 使用 StatefulWidget
利用 StatefulWidget
和 setState()
方法,可以在更新数据时重新构建列表。
示例:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: DynamicList(),
);
}
}
class DynamicList extends StatefulWidget {
@override
_DynamicListState createState() => _DynamicListState();
}
class _DynamicListState extends State<DynamicList> {
List<String> items = ['Item 1', 'Item 2', 'Item 3'];
void _addItem() {
setState(() {
items.add('Item ${items.length + 1}');
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Dynamic List')),
body: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index]),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: _addItem,
child: Icon(Icons.add),
),
);
}
}
2. 使用 Provider
或其他状态管理
如果你的应用较复杂,使用状态管理库(如 Provider
、Bloc
、Riverpod
等)可以更好地管理状态和更新列表视图。
示例(使用 Provider):
首先,添加 provider
依赖到 pubspec.yaml
:
dependencies:
provider: ^6.0.0
然后,创建一个 ChangeNotifier
类来管理状态:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => ItemList(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: DynamicList(),
);
}
}
class ItemList extends ChangeNotifier {
List<String> items = ['Item 1', 'Item 2', 'Item 3'];
void addItem() {
items.add('Item ${items.length + 1}');
notifyListeners();
}
}
class DynamicList extends StatelessWidget {
@override
Widget build(BuildContext context) {
final itemList = Provider.of<ItemList>(context);
return Scaffold(
appBar: AppBar(title: Text('Dynamic List')),
body: ListView.builder(
itemCount: itemList.items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(itemList.items[index]),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
itemList.addItem();
},
child: Icon(Icons.add),
),
);
}
}
3. 使用 StreamBuilder
如果数据来自异步源(如网络请求或数据库),可以使用 StreamBuilder
来动态更新列表。
示例:
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: StreamList(),
);
}
}
class StreamList extends StatelessWidget {
final StreamController<String> _controller = StreamController<String>();
void _addItem() {
_controller.sink.add('Item ${DateTime.now()}');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Stream List')),
body: StreamBuilder<String>(
stream: _controller.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListTile(title: Text(snapshot.data!));
}
return Center(child: Text('No items'));
},
),
floatingActionButton: FloatingActionButton(
onPressed: _addItem,
child: Icon(Icons.add),
),
);
}
@override
void dispose() {
_controller.close();
super.dispose();
}
}
14. stream 是什么?
在 Flutter 和 Dart 中,Stream 是一种用于处理异步数据流的机制。它允许你接收一系列异步事件,而不仅仅是单个值。Streams 非常适合处理动态数据源,例如用户输入、网络请求、文件读取等。
主要特点
- 异步数据处理:
- Stream 允许你以异步的方式接收数据,不会阻塞当前线程。这使得应用在处理数据时仍然能够保持响应。
- 多个值:
- 与
Future
只返回一个单一值不同,Stream 可以发送多个值,可以是事件、消息或数据。
- 与
- 监听:
- 你可以通过添加监听器(Listener)来接收来自 Stream 的数据。每当 Stream 中有新数据可用时,监听器会被调用。
Stream 的类型
- 单订阅 Stream:
- 只能有一个订阅者,适合处理一次性事件流,例如从文件读取数据。
- 广播 Stream:
- 可以有多个订阅者,适合处理需要广播给多个监听者的事件,例如用户输入或网络请求。
基本使用示例
以下是一个简单的 Stream 使用示例,展示了如何创建和监听 Stream:
import 'dart:async';
void main() {
// 创建一个单订阅 Stream
Stream<int> numberStream = Stream<int>.periodic(Duration(seconds: 1), (count) => count);
// 监听 Stream
numberStream.listen((number) {
print('Received number: $number');
});
}
停止监听
你可以通过调用 cancel()
方法来停止监听 Stream:
import 'dart:async';
void main() {
Stream<int> numberStream = Stream<int>.periodic(Duration(seconds: 1), (count) => count);
var subscription = numberStream.listen((number) {
print('Received number: $number');
if (number >= 5) {
subscription.cancel(); // 停止监听
}
});
}
使用 StreamBuilder
在 Flutter 中,通常使用 StreamBuilder
来构建 UI,自动响应 Stream 的数据变化。下面是一个使用 StreamBuilder
的示例:
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: StreamExample(),
);
}
}
class StreamExample extends StatelessWidget {
final Stream<int> numberStream = Stream<int>.periodic(Duration(seconds: 1), (count) => count);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Stream Example')),
body: Center(
child: StreamBuilder<int>(
stream: numberStream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
return Text('Received number: ${snapshot.data}');
}
},
),
),
);
}
}
15. keys 在 Flutter 中是什么,你什么时候应该使用它?
在 Flutter 中,Keys 是一个用于标识 Widget 的对象,帮助 Flutter 识别和管理 Widget 的状态。Keys 在构建和更新 Widget 时起到重要作用,尤其是在涉及到状态管理、列表和动画时。
Keys 的类型
- GlobalKey:
- 用于跨 Widget 树访问状态。可以在不同的地方引用同一个 Widget 的状态。
- 示例:在页面间导航时,保持表单状态。
- ValueKey:
- 根据给定的值来识别 Widget,通常用于列表中的元素。
- 示例:在列表中修改顺序时,确保正确更新每个元素的状态。
- ObjectKey:
- 通过对象的引用来识别 Widget,适用于需要比较对象的情况。
- UniqueKey:
- 每次创建时都会生成一个唯一的 Key,适合临时 Widget。
使用场景
列表的动态更新:
- 当你在列表中添加、删除或重新排序项时,使用 Keys 可以帮助 Flutter 确定哪些 Widget 需要更新,从而避免不必要的重建。
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
key: ValueKey(items[index]), // 使用 ValueKey
title: Text(items[index]),
);
},
);
保持状态:
- 使用
GlobalKey
时,可以在 Widget 重建时保留其状态。例如,在使用Form
组件时,可以通过GlobalKey
访问表单状态。
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
Form(
key: _formKey,
child: TextFormField(
// ...
),
);
动画和过渡:
- 在使用动画时,Keys 可以帮助 Flutter 确定哪个 Widget 应该保持其状态或动画效果,从而实现更流畅的过渡。
构建条件 Widget:
- 当根据某些条件创建 Widget 时,使用 Keys 可以确保 Flutter 正确管理这些 Widget 的状态。
16. GlobalKeys 是什么?
在 Flutter 中,GlobalKey
是一种特殊的 Key,用于跨 Widget 树访问状态和方法。GlobalKey
允许你在不同的 Widget 之间共享状态,特别是在使用 StatefulWidget
时。它的主要用途是确保在 Widget 树重建时仍能保留和访问 Widget 的状态。
GlobalKey
的特点
- 跨 Widget 树访问:
GlobalKey
允许你从不同的地方访问同一个 Widget 的状态。使用GlobalKey
,你可以在 Widget 的外部调用其状态方法,比如在表单中验证字段。
- 唯一性:
- 每个
GlobalKey
都是唯一的,因此 Flutter 能够确保在 Widget 树中识别每个 Widget。
- 每个
- 持久性:
- 当 Widget 被重建时,
GlobalKey
保持对其状态的引用,因此可以在重建过程中保持状态。
- 当 Widget 被重建时,
使用场景
- 表单状态管理:
- 在处理表单时,你可以使用
GlobalKey
来访问和验证表单的状态。
- 在处理表单时,你可以使用
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState?.validate() ?? false) {
// 处理表单提交
}
},
child: Text('Submit'),
),
],
),
);
}
- 控制 Widget 的状态:
- 使用
GlobalKey
可以直接控制 Widget 的状态方法,比如在动画中调用。
- 使用
- 在复杂布局中保持状态:
- 当你的 Widget 树结构复杂,且多个子 Widget 可能会被重建时,
GlobalKey
可以帮助你保持子 Widget 的状态。
- 当你的 Widget 树结构复杂,且多个子 Widget 可能会被重建时,
17. 何时应使用 mainAxisAlignment 和 crossAxisAlignment?
18. 你什么时候可以使用 double.INFINITY ?
当你希望该小部件的大小与父小部件相同,请允许
19. Ticker 、 Tween 和 AnimationController 是什么?
在 Flutter 中,Ticker
、Tween
和 AnimationController
是用于实现动画的关键组件。它们各自有不同的角色和功能,下面是对它们的详细解释:
1. Ticker
- 定义:
Ticker
是一个用于生成时间片的对象,它会在每一帧(frame)中调用一个回调函数。它通常与动画相关联,并用于控制动画的更新频率。 - 工作原理:
Ticker
会在每一帧调用回调,并提供当前的时间戳。你可以使用这个时间戳来更新动画的状态。
- 使用场景:
- 通常在自定义动画或使用
AnimationController
时,Ticker
是由AnimationController
自动创建和管理的。
- 通常在自定义动画或使用
2. Tween
- 定义:
Tween
是一个用于定义动画起始值和结束值的对象。它帮助你在动画的不同状态之间插值(interpolate)。 - 工作原理:
Tween
接受两个值,分别是起始值和结束值,然后在这两个值之间生成中间值。你可以使用Tween
来处理各种类型的值,例如颜色、尺寸、位置等。
- 使用示例:
Tween<double> tween = Tween<double>(begin: 0.0, end: 1.0); double value = tween.transform(0.5); // value = 0.5
3. AnimationController
- 定义:
AnimationController
是一个特殊的Animation
,它可以控制动画的播放。它负责管理动画的生命周期,包括启动、停止、反转等。 - 工作原理:
AnimationController
需要一个vsync
参数,这通常是SingleTickerProviderStateMixin
或TickerProviderStateMixin
的实例。它生成一个从 0.0 到 1.0 的值,表示动画的进度。
- 使用场景:
- 在需要控制动画的开始、停止和反转时使用
AnimationController
。
- 在需要控制动画的开始、停止和反转时使用
示例:结合使用 Ticker、Tween 和 AnimationController
以下是一个简单的示例,展示如何使用 Ticker
、Tween
和 AnimationController
创建一个动画:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: AnimatedBox());
}
}
class AnimatedBox extends StatefulWidget {
@override
_AnimatedBoxState createState() => _AnimatedBoxState();
}
class _AnimatedBoxState extends State<AnimatedBox> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_animation = Tween<double>(begin: 0.0, end: 300.0).animate(_controller);
_controller.forward(); // 启动动画
}
@override
void dispose() {
_controller.dispose(); // 释放资源
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Animation Example')),
body: Center(
child: AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Container(
width: _animation.value,
height: _animation.value,
color: Colors.blue,
);
},
),
),
);
}
}
- Ticker:用于生成时间片,通常与动画相关联。
- Tween:定义动画的起始值和结束值,用于在值之间插值。
- AnimationController:控制动画的播放,包括启动、停止和反转。它生成一个时间值(通常在 0.0 到 1.0 之间),用于与
Tween
结合使用。
20. ephemeral 状态是什么?
在 Flutter 中,ephemeral 状态(短暂状态)指的是一种状态,它是局部的、短期的,并且只在当前 Widget 的生命周期内有效。这种状态通常不需要持久化,也不需要在 Widget 树之外共享。
特点
- 局部性:
- Ephemeral 状态通常只与一个特定的 Widget 相关联。它不会影响其他 Widget。
- 短暂性:
- 该状态在 Widget 被创建时存在,在 Widget 被销毁时消失。它不需要跨多个 Widget 或屏幕保持。
- 使用
StatefulWidget
:- Ephemeral 状态通常通过
StatefulWidget
来管理。StatefulWidget
的State
对象可以包含所有的局部状态。
- Ephemeral 状态通常通过
使用场景
- 用户输入:例如,文本框的内容、复选框的选中状态等。
- 动画状态:例如,动画的当前进度或状态。
- UI 状态:例如,按钮的启用和禁用状态、加载指示器的可见性等。
示例
下面是一个简单的例子,演示如何在 StatefulWidget
中管理 ephemeral 状态:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: Counter());
}
}
class Counter extends StatefulWidget {
@override
_CounterState createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _count = 0; // 这是一个 ephemeral 状态
void _incrementCounter() {
setState(() {
_count++; // 更新局部状态
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Ephemeral State Example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('You have pushed the button this many times:'),
Text(
'$_count',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Ephemeral 状态是 Flutter 中一种局部且短期存在的状态,适合用于管理特定 Widget 的状态。它通过 StatefulWidget
管理,适用于用户输入、动画和 UI 状态等场景。与之相对的是 app 状态(应用状态),后者是需要在多个 Widget 之间共享的持久状态。
小结
感谢阅读本文
如果有什么建议,请在评论中让我知道。我很乐意改进。
猫哥 APP
flutter 学习路径
- Flutter 优秀插件推荐
- Flutter 基础篇1 - Dart 语言学习
- Flutter 基础篇2 - 快速上手
- Flutter 实战1 - Getx Woo 电商APP
- Flutter 实战2 - 上架指南 Apple Store、Google Play
- Flutter 基础篇3 - 仿微信朋友圈
- Flutter 实战3 - 腾讯即时通讯 第一篇
- Flutter 实战4 - 腾讯即时通讯 第二篇
© 猫哥 ducafecat.com
end