Flutter開發者必備面試問題與答案02

独立开发者_猫哥發表於2024-10-28

Flutter開發者必備面試問題與答案02

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 提供了一些生命週期方法,如 didChangeDependenciesdispose,用於在頁面進入和退出時執行特定操作。

使用示例

下面是一個使用 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 中,asyncawaitFuture 是處理非同步程式設計的關鍵概念。它們可以幫助你編寫非阻塞的程式碼,使得應用能夠在執行長時間執行的任務時保持響應。以下是對這三個概念的詳細解釋:

1. Future

  • 定義Future 是一個表示可能在未來某個時間點完成的非同步操作的物件。它可以用於表示一個延遲的值,通常用於處理非同步任務的結果。
  • 狀態

    • 未完成(Pending):Future 仍在進行中。
    • 已完成(Completed):Future 執行完畢,可以獲取結果。
    • 已失敗(Error):Future 執行過程中出現錯誤。
  • 使用示例

    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');
    }

整體示例

下面是一個完整的示例,展示瞭如何使用 asyncawaitFuture

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 完成,並獲取其結果。

透過使用 asyncawaitFuture,你可以輕鬆地處理非同步操作,使程式碼更加簡潔和可讀。


13. 你如何動態更新列表檢視?

在 Flutter 中,動態更新列表檢視通常使用 ListView 元件結合狀態管理來實現。以下是幾種常見的方法來動態更新列表檢視:

1. 使用 StatefulWidget

利用 StatefulWidgetsetState() 方法,可以在更新資料時重新構建列表。

示例:
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 或其他狀態管理

如果你的應用較複雜,使用狀態管理庫(如 ProviderBlocRiverpod 等)可以更好地管理狀態和更新列表檢視。

示例(使用 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 保持對其狀態的引用,因此可以在重建過程中保持狀態。

使用場景

  • 表單狀態管理

    • 在處理表單時,你可以使用 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 的狀態。

17. 何時應使用 mainAxisAlignment 和 crossAxisAlignment?

mainAxisAlignment


18. 你什麼時候可以使用 double.INFINITY ?

當你希望該小部件的大小與父小部件相同,請允許


19. Ticker 、 Tween 和 AnimationController 是什麼?

在 Flutter 中,TickerTweenAnimationController 是用於實現動畫的關鍵元件。它們各自有不同的角色和功能,下面是對它們的詳細解釋:

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 引數,這通常是 SingleTickerProviderStateMixinTickerProviderStateMixin 的例項。它生成一個從 0.0 到 1.0 的值,表示動畫的進度。
  • 使用場景

    • 在需要控制動畫的開始、停止和反轉時使用 AnimationController

示例:結合使用 Ticker、Tween 和 AnimationController

以下是一個簡單的示例,展示如何使用 TickerTweenAnimationController 建立一個動畫:

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 來管理。StatefulWidgetState 物件可以包含所有的區域性狀態。

使用場景

  • 使用者輸入:例如,文字框的內容、核取方塊的選中狀態等。
  • 動畫狀態:例如,動畫的當前進度或狀態。
  • 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

相關文章