如何在 Flutter 中簡單實現滑順的 Light/Dark 模式切換

更新於 2024/12/18閱讀時間約 9 分鐘


raw-image

許多 App 都會支持 Light 模式與 Dark 模式,增加使用者體驗,為了做到這個功能,我們可以使用內建 Theme 加上 StatefulWidget 或其他狀態管理套件,就可以輕鬆完成 Light 模式與 Dark 模式。讓我們看看一個簡單的例子。

簡單的切換 Theme

main() {
runApp(const MainApp());
}

class MainApp extends StatefulWidget {
const MainApp({super.key});
@override
State<MainApp> createState() => _MainAppState();
}

class _MainAppState extends State<MainApp> {
bool isDark = false;
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: isDark ? ThemeData.dark() : ThemeData.light(),
home: Scaffold(
body: const Center(
child: Text("Hello World"),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.refresh),
onPressed: () => setState(() => isDark = !isDark),
),
),
);
}
}

我們在 StatefulWidget 中維護了 isDark 變數,當使用者按下左下角按鈕時,就會更新 isDark,並設定不同模式的 ThemeData 給 MaterialApp,藉此達到切換 Light / Dark 模式的效果,而在切換過程中,Flutter 也會幫我們用漸變的方式切換主題,而不是一瞬間就切換完成,增加視覺效果。

raw-image

自定義的 ThemeExtension

有些時候,當我們的設計不是遵循 Material Design 的話,Flutter 提供的 Theme 就會不足以完成我們的需求,此時我們就會使用 ThemeExtension 的功能來擴充 Theme,讓整個 App 都可以使用一致的設計。下面是一段我們自定義的 ThemeExtension,其中除了自定義的顏色設計之外,我們還需要實作 copyWith 與 lerp 方法。

class MyThemeExtension extends ThemeExtension<MyThemeExtension> {
final Color backgroundColor;

MyThemeExtension(this.backgroundColor);

@override
ThemeExtension<MyThemeExtension> copyWith() {
// TODO: implement copyWith
throw UnimplementedError();
}

@override
ThemeExtension<MyThemeExtension> lerp(covariant MyThemeExtension other, double t) {
// TODO: implement lerp
throw UnimplementedError();
}
}

還記得剛剛提到的,切換 Light / Dark 模式時,Flutter 會用漸變的方式,讓畫面漸漸的從 Light 模式轉變為 Dark 模式 (其實動畫時間很短,一下次就轉換完成 XD),為了讓自定義的 ThemeExtension 也能享受到這個效果,實作 lerp 方法就很重要了。lerp 方法會傳入要轉換的 Theme 與動畫時間,讓我們可以自行決定顏色在轉換過程中如何變化,在這邊我們簡單地使用 Color.lerp 來協助做線性轉換即可。

@override
ThemeExtension<MyThemeExtension> lerp(covariant MyThemeExtension other, double t) {
return MyThemeExtension(
Color.lerp(backgroundColor, other.backgroundColor, t)!,
);
}

當我們實作了 lerp 方法之後,再次打開 App 切換 Light / Dark 模式,會發現我們自定義的顏色也能在切換過程中有平滑的視覺效果。

raw-image

完整效果請參考這邊

更酷炫的切換動畫

在研究 Light / Dark 模式切換的過程中,發現了很特別切換效果,詳細可以參考這邊。當使用者切換模式時,畫面會由左上角開始轉換,並往右下角輻射,直到所有畫面都轉換完成,讓我們直接看看下面圖片。

raw-image

與 Flutter 預設的切換方式不同,這種切換方式更令人眼睛一亮,讓我們來看看這是如何做到的。其實要做到這個效果也並不複雜,主要原理是使用 Stack + ClipPath 來完成,簡單來說就是,先在 Stack 中疊上 Light 模式 Widget,然後再疊上被 ClipPath 裁切過的 Dark 模式 Widget,最後透過動畫來慢慢放大被裁切的 Dark 模式 Widget,最後填滿的畫面。如此一來,就能完成這個酷炫的 Light / Dark 模式切換效果。

@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animationController,
builder: (BuildContext context, Widget? child) {
return Stack(
children: [
Theme(
data: _getTheme(!widget.isDark),
child: widget.builder(context),
),
ClipPath(
clipper: CircularClipper(
1.5 * MediaQuery.of(context).size.height * _animationController.value,
Offset.zero,
),
child: Theme(
data: _getTheme(widget.isDark),
child: widget.builder(context),
),
),
],
);
},
);
}

完整效果請參考這邊

小結

為 App 增加一些轉場特效,像是我們今天分享的 Light / Dark 模式切換,或者是 Routing 時的 Transition 效果,都能有效增加使用者體驗,讓 App 看起來更加精緻。

分享各種軟體開發技巧與心得
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
本文探討如何使用 Flutter 的 Widget 測試來驗證應用程式的 Routing 功能,確保重構後仍然正常運作。我們將通過具體的範例,從設定 MockNavigatorObserver 到驗證 Routing 參數,提供清晰步驟與建議,以提高測試的可讀性和效能,是開發人員必備的測試技巧。
這篇文章介紹如何在多臺 MacBook 上同步開發工具與設定,以提高開發效率。文章重點在於如何同步 IntelliJ、IdeaVim 和 Alfred 配置,並解決因設定不同而影響開發效率的問題。透過簡單的步驟,開發者可以在不同設備上無縫運作,持續專注於開發工作,而不必因為熱鍵或工具失效而浪費時間。
本文探討在客戶端程式開發中,如何有效處理根據後端不同資料型態變化的畫面顯示。透過列舉 Shortgun Surgery 問題及其對代碼維護的影響,分析各種設計模式,包括轉接器模式和策略模式,來改善資料的處理方式。最終提出根據具體情況選擇合適解法的重要性,以確保開發效率與代碼可維護性。
本文介紹如何利用 Flutter 框架和 Flame 遊戲引擎製作一個簡單的點擊小遊戲。從導入 Flame 套件,到使用 GameWidget,接著為遊戲中的騎士角色添加動作,最後實現點擊計數功能,這篇文章一步步帶你體驗遊戲開發的樂趣,讓你掌握基本的遊戲設計和邏輯編程技巧。
本文分享瞭如何使用 Notion Web Clipper 來儲存文章,並結合 Habit Stacking 技術克服維持新習慣的困難。同時探討如何使用 Flutter Web 和 Notion API 開發自用的 Chrome 擴展功能,提升個人資訊管理和閱讀效率。
Flutter Widget 能加速開發,但誤用 MediaQuery 可能導致不預期的重建。範例中,頁面因鍵盤觸發高度變動而刷新,隨機數重新生成。使用固定比例設計避免重建,顯示深入理解框架對穩定性的重要性。
本文探討如何使用 Flutter 的 Widget 測試來驗證應用程式的 Routing 功能,確保重構後仍然正常運作。我們將通過具體的範例,從設定 MockNavigatorObserver 到驗證 Routing 參數,提供清晰步驟與建議,以提高測試的可讀性和效能,是開發人員必備的測試技巧。
這篇文章介紹如何在多臺 MacBook 上同步開發工具與設定,以提高開發效率。文章重點在於如何同步 IntelliJ、IdeaVim 和 Alfred 配置,並解決因設定不同而影響開發效率的問題。透過簡單的步驟,開發者可以在不同設備上無縫運作,持續專注於開發工作,而不必因為熱鍵或工具失效而浪費時間。
本文探討在客戶端程式開發中,如何有效處理根據後端不同資料型態變化的畫面顯示。透過列舉 Shortgun Surgery 問題及其對代碼維護的影響,分析各種設計模式,包括轉接器模式和策略模式,來改善資料的處理方式。最終提出根據具體情況選擇合適解法的重要性,以確保開發效率與代碼可維護性。
本文介紹如何利用 Flutter 框架和 Flame 遊戲引擎製作一個簡單的點擊小遊戲。從導入 Flame 套件,到使用 GameWidget,接著為遊戲中的騎士角色添加動作,最後實現點擊計數功能,這篇文章一步步帶你體驗遊戲開發的樂趣,讓你掌握基本的遊戲設計和邏輯編程技巧。
本文分享瞭如何使用 Notion Web Clipper 來儲存文章,並結合 Habit Stacking 技術克服維持新習慣的困難。同時探討如何使用 Flutter Web 和 Notion API 開發自用的 Chrome 擴展功能,提升個人資訊管理和閱讀效率。
Flutter Widget 能加速開發,但誤用 MediaQuery 可能導致不預期的重建。範例中,頁面因鍵盤觸發高度變動而刷新,隨機數重新生成。使用固定比例設計避免重建,顯示深入理解框架對穩定性的重要性。
你可能也想看
Google News 追蹤
Thumbnail
本文探討了複利效應的重要性,並藉由巴菲特的投資理念,說明如何選擇穩定產生正報酬的資產及長期持有的核心理念。透過定期定額的投資方式,不僅能減少情緒影響,還能持續參與全球股市的發展。此外,文中介紹了使用國泰 Cube App 的便利性及低手續費,幫助投資者簡化投資流程,達成長期穩定增長的財務目標。
Thumbnail
這篇內容,將會講解什麼是「switch」,以及與「switch」相關的知識。包括switch的簡介、switch、break。
在上篇瞭解完Actions & Blinks的整個工作流程後,我們就來學習如何用程式碼構建屬於自己的Actions & Blinks吧!本篇詳細講解了要自製Actions & Blinks的所有步驟並提供範例程式碼給大家參考,相信能讓大家快速入門!
Thumbnail
不論是 Astra、Blocksy 還是 Kadence 佈景主題,都有內建頁首與頁尾編輯器,你可以在外觀自訂器中以所見即所得的方式新增各種元素,像是選單、按鈕及社群圖示。
Thumbnail
這個系列的教學會列出基本上所有常見的造型和一些基礎 , 讓各位可以自行搭配造型~在這個第四篇呢 , 我們會來一起了解:常見的窗口 / 框架造型定義他們的意思加了之後有什麼效果那就讓我們開始吧!
Thumbnail
# 簡介 身為一位專注於 Vue.js 的前端開發者,這是我第一次嘗試構建 Flutter 網頁應用。讓我們開始吧! ## 第一次嘗試 ### 第一步:創建一個 Flutter 應用 首先,通過運行以下命令來創建一個新的 Flutter 項目: ```sh flutter
setter和getter能把狀態改變時需做的事情包裝起來,讓外部只需簡單修改參數就能達到預想的效果
Thumbnail
如何透過 CSS 來美化和增強文本的可讀性,對於提升用戶體驗至關重要。本文將介紹如何使用 CSS 來處理網頁上的文本,包括字型設定、文本排列、裝飾等多方面。
Thumbnail
有沒有想過,即使沒有任何編程背景,你的創意也能在六個月內轉化成真實的App?我可以以自身經歷跟你說有了 No-Code Tool (無代碼工具) 和 AI 的幫助,這一切都是可能的!你一行 code 都不需要打,甚至也無須學習任何編程語言!沒有什麼比實踐一個自小認為不可能的任務還振奮人心的事了!
Thumbnail
本文探討了複利效應的重要性,並藉由巴菲特的投資理念,說明如何選擇穩定產生正報酬的資產及長期持有的核心理念。透過定期定額的投資方式,不僅能減少情緒影響,還能持續參與全球股市的發展。此外,文中介紹了使用國泰 Cube App 的便利性及低手續費,幫助投資者簡化投資流程,達成長期穩定增長的財務目標。
Thumbnail
這篇內容,將會講解什麼是「switch」,以及與「switch」相關的知識。包括switch的簡介、switch、break。
在上篇瞭解完Actions & Blinks的整個工作流程後,我們就來學習如何用程式碼構建屬於自己的Actions & Blinks吧!本篇詳細講解了要自製Actions & Blinks的所有步驟並提供範例程式碼給大家參考,相信能讓大家快速入門!
Thumbnail
不論是 Astra、Blocksy 還是 Kadence 佈景主題,都有內建頁首與頁尾編輯器,你可以在外觀自訂器中以所見即所得的方式新增各種元素,像是選單、按鈕及社群圖示。
Thumbnail
這個系列的教學會列出基本上所有常見的造型和一些基礎 , 讓各位可以自行搭配造型~在這個第四篇呢 , 我們會來一起了解:常見的窗口 / 框架造型定義他們的意思加了之後有什麼效果那就讓我們開始吧!
Thumbnail
# 簡介 身為一位專注於 Vue.js 的前端開發者,這是我第一次嘗試構建 Flutter 網頁應用。讓我們開始吧! ## 第一次嘗試 ### 第一步:創建一個 Flutter 應用 首先,通過運行以下命令來創建一個新的 Flutter 項目: ```sh flutter
setter和getter能把狀態改變時需做的事情包裝起來,讓外部只需簡單修改參數就能達到預想的效果
Thumbnail
如何透過 CSS 來美化和增強文本的可讀性,對於提升用戶體驗至關重要。本文將介紹如何使用 CSS 來處理網頁上的文本,包括字型設定、文本排列、裝飾等多方面。
Thumbnail
有沒有想過,即使沒有任何編程背景,你的創意也能在六個月內轉化成真實的App?我可以以自身經歷跟你說有了 No-Code Tool (無代碼工具) 和 AI 的幫助,這一切都是可能的!你一行 code 都不需要打,甚至也無須學習任何編程語言!沒有什麼比實踐一個自小認為不可能的任務還振奮人心的事了!