前一篇文章,我介紹了組合和自繪這兩種自定義Widget的方式。對於組裝,咱們按照從上到下、從左到右的佈局順序去分解目標視圖,將基本的Widget封裝到Column、Row中,從而合成更高級別的Widget;而對於自繪,咱們則經過承載繪製邏輯的載體CustomPainter,在其paint方法中使用畫筆Paint與畫布Canvas,繪製不一樣風格、不一樣類型的圖形,從而實現基於自繪的自定義組件。
javascript
對於一個產品來講,在業務早期其實更多的是處理基本功能有和無的問題:工程師來負責實現功能,PM負責功能好用很差用。在產品的基本功能已經完善,作到了六七十分的時候,再往上作增加就須要運營來介入了。
css
在這其中,如何經過用戶分層去實現App的個性化是常見的增加運營手段,而主題樣式更換則是實現個性化中的一項重要技術手段。java
好比,微博、UC瀏覽器和電子書客戶端都提供了對夜間模式的支持,而淘寶、京東這樣的電商類應用,還會在特定的電商活動日自動更新主題樣式,就連如今的手機操做系統也提供了系統級切換展現樣式的能力。
瀏覽器
那麼,這些在應用內切換樣式的功能是如何實現的呢?在Flutter中,在普通的應用上增長切換主題的功能又要作哪些事情呢?
微信
主題定製
app
主題,又叫皮膚、配色,通常由顏色、圖片、字號、字體等組成,咱們能夠把它看做是視覺效果在不一樣場景下的可視資源,以及相應的配置集合。好比,App的按鈕,不管在什麼場景下都須要背景圖片資源、字體顏色、字號大小等,而所謂的主題切換,只是在不一樣的主題之間更新這些資源及配置集合而已。
組件化
所以在App開發中,咱們一般不關心資源和配置的視覺效果好很差看,只要關心資源提供的視覺內容能不能用。好比,對於圖片資源,咱們並不須要關心它渲染出來的實際效果,只須要肯定她渲染出來是一張固定寬高尺寸的區域,不影響頁面佈局,能把業務流程跑通便可。
佈局
視覺效果是易變的,咱們將這些變化的部分抽離出來,把提供不一樣視覺效果的資源和配置按照主題進行歸類,整合到一個統一的中間層去管理,這樣咱們就能實現主題的管理和切換了。
字體
在iOS中,咱們一般會將主題的配置信息預先寫到plist文件中,經過一個單例來控制APP應該使用哪一種配置。Flutter也提供了相似的能力,由ThemeData來統一管理主題的配置信息。
spa
ThemeData涵蓋了Material Design規範的可自定義部分樣式,好比應用明暗模式brightness、應用主色調primaryColor、應用次級色調accentColor、文本字體fontFamily、輸入框光標顏色curcorColor等。
經過ThemeData來自定義應用主題,咱們能夠實現App全局範圍,或是Widget局部範圍的樣式切換。
全局統一的視覺風格定製
在Flutter中,應用程序類MaterialApp的初始化方法,爲咱們提供了設置主題的能力。咱們能夠經過參數theme,選擇改變App的主題色、字體等,設置界面在Material下的展現樣式。
如下代碼演示瞭如何設置App全局範圍主題。在這段代碼中,咱們設置了App的明暗模式brightness爲暗色,主色調爲青色:
MaterialApp( title: 'Flutter Demo',// 標題 theme: ThemeData(// 設置主題 brightness: Brightness.dark,// 明暗模式爲暗色 primaryColor: Colors.cyan,// 主色調爲青色 ), home: MyHomePage(title: 'Flutter Demo Home Page'),);
運行效果以下:
能夠看到,咱們雖然只修改了主色調primaryColor和明暗模式brightness兩個參數,但按鈕、文字顏色都隨之調整了。這是由於默認狀況下,ThemeData中不少其餘次級視覺屬性,都會受到主色調與明暗模式的影響。若是咱們想要精確控制它們的展現樣式,須要再細化一下主題配置。
下面的例子中,咱們將icon的顏色調整爲黃色,文字顏色調整爲紅色,按鈕顏色調整爲黑色:
MaterialApp( title: 'Flutter Demo',// 標題 theme: ThemeData(// 設置主題 brightness: Brightness.dark,// 設置明暗模式爲暗色 accentColor: Colors.black,//(按鈕)Widget 前景色爲黑色 primaryColor: Colors.cyan,// 主色調爲青色 iconTheme:IconThemeData(color: Colors.yellow),// 設置 icon 主題色爲黃色 textTheme: TextTheme(body1: TextStyle(color: Colors.red))// 設置文本顏色爲紅色 ), home: MyHomePage(title: 'Flutter Demo Home Page'),);
運行一下,能夠看到,圖標、文字、按鈕的顏色都隨之更改了。
局部獨立的視覺風格定製
爲整個APP提供統一的視覺呈現效果當然頗有必要,但有時咱們但願爲某個頁面、或者某個區塊設置不一樣於APP風格的展示樣式。以主題切換功能爲例,咱們但願爲不一樣的主題提供不一樣的展現預覽。
在Flutter中,咱們可使用Theme來對App的主題進行局部覆蓋。Theme是一個單子Widget容器,與MaterialApp相似的,咱們能夠設置其data屬性,對其子Widget進行樣式定製:
若是咱們不想繼承任何App全局的顏色或字體樣式,能夠直接新建一個ThemeData實例,依次設置對應的樣式。
若是咱們不想在局部重寫全部的樣式,則能夠繼承App的主題,使用copyWith方法,只更新部分樣式。
下面的代碼演示了這兩種方式的用法:
Row( children: <Widget>[Icon(Icons.aspect_ratio), Text("Icon全局主題")], ), Theme( data: ThemeData(iconTheme: IconThemeData(color: Colors.green)), child: Row( children: <Widget>[Icon(Icons.aspect_ratio), Text("Icon局部新建主題")], ), ), Theme( data: Theme.of(context) .copyWith(iconTheme: IconThemeData(color: Colors.blue)), child: Row( children: <Widget>[Icon(Icons.aspect_ratio), Text("Icon局部繼承主題")], ), )
上例表名,不論是直接新建一個ThemeData,仍是賦值全局主題而後作修改,均可以實現全局主題的覆蓋。像這樣使用局部主題覆蓋全局主題的方式,在Flutter中是一種常見的自定義子Widget展現樣式的方法。
除了定義Material Design規範中那些可自定義部分樣式外,主題的另外一個重要用途是樣式複用。
好比,咱們想要爲某個子Widget的背景色複用App的主題色,咱們就能夠經過 Theme.of(context) 方法,取出對應的屬性,應用到這段文字的樣式中。
Theme.of(context) 方法將向上查找Widget樹,並返回Widget樹中最近的主題Theme。若是Widget的父Widget們有一個單獨的主題定義,則使用該主題。若是不是,那就使用App的全局主題。
在下面的例子中,咱們建立了一個包裝了一個Text組件的Container容器。在Container的背景色定義中,咱們複用了App的主題色:
Container( color: Theme.of(context).primaryColor, child: Text("Text With a background Color"), )
分平臺主題定製
有時候,爲了知足不一樣平臺的用戶需求,咱們但願針對特定的平臺設置不一樣的樣式。好比,在iOS平臺上設置淺色主題,在Android平臺上設置深色主題。面對這樣的需求,咱們能夠根據 defaultTargetPlatform 來判斷當前應用所運行的平臺,從而根據系統類型來設置對應的主題。
在下面的例子中,咱們爲iOS和Android分別設置了兩個主題。在MaterialApp的初始化方法中,咱們根據平臺類型,設置了不一樣的主題:
// iOS 淺色主題final ThemeData kIOSTheme = ThemeData( brightness: Brightness.light,// 亮色主題 accentColor: Colors.white,//(按鈕)Widget 前景色爲白色 primaryColor: Colors.blue,// 主題色爲藍色 iconTheme:IconThemeData(color: Colors.grey),//icon 主題爲灰色 textTheme: TextTheme(body1: TextStyle(color: Colors.black))// 文本主題爲黑色);// Android 深色主題final ThemeData kAndroidTheme = ThemeData( brightness: Brightness.dark,// 深色主題 accentColor: Colors.black,//(按鈕)Widget 前景色爲黑色 primaryColor: Colors.cyan,// 主題色 Wie 青色 iconTheme:IconThemeData(color: Colors.blue),//icon 主題色爲藍色 textTheme: TextTheme(body1: TextStyle(color: Colors.red))// 文本主題色爲紅色);// 應用初始化MaterialApp( title: 'Flutter Demo', theme: defaultTargetPlatform == TargetPlatform.iOS ? kIOSTheme : kAndroidTheme,// 根據平臺選擇不一樣主題 home: MyHomePage(title: 'Flutter Demo Home Page'),);
運行後,iOS端展現效果以下:
Android端展現效果以下:
固然,除了設置主題以外,你也能夠利用 defaultTargetPlatform == TargetPlatform.iOS 這個判斷去實現一些其餘須要判斷平臺的邏輯,好比在界面上使用更符合Android或者iOS設計風格的組件。
總結
主題設置數據App開發的高級特性,歸根結底實際上是提供了一種視覺資源與視覺配置的管理機制。與其餘平臺相似,Flutter也提供了集中式管理主題的機制,能夠在遵循Material Design規範的ThemeData中,定義那些可定製化的樣式。
咱們既能夠經過設置MaterialApp全局主題實現應用總體視覺風格的統一,也能夠經過Theme單子Widget容器使用局部主題覆蓋全局主題,實現局部獨立的視覺風格。
除此以外,在自定義組件化過程當中,咱們還能夠使用Theme.of方法取出主題對應的屬性值,從而實現多種組件在視覺風格上的複用。
面對常見的平臺設置主題場景,咱們能夠根據defaultTargetPlatform來精確識別當前應用所處的系統,從而配置應用的主題。
以上
本文分享自微信公衆號 - iOS小生活(iOSHappyLife)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。