Flutter開發者必備面試問題與答案02
影片
https://youtu.be/XYSxTb0iA9I
https://www.bilibili.com/video/BV1Zk2dYyEBr/
前言
原文 Flutter 完整面試問題及答案02
本文是 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
- SaaS Fast
- Flutter GetX Generator
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