在開始以前的提示:雖然Flutter背靠Google這棵大樹,可是畢竟仍是一個年輕的技術,項目還處於初期階段,更新很是快,問題也不是通常的多,使用Flutter的話就意味着必須忍受各類奇怪的bug和沒有豐富中文資料的頭疼,本文不是安利同窗們入坑,只是對「極簡詩詞」app的開發過程進行記錄。html
另外app已經上架,有興趣的同窗能夠下載試試:www.coolapk.com/apk/251155json
主要界面截圖:bash
主頁 | 暗黑版主頁 | 詩集 | 詩集瀏覽 |
---|---|---|---|
![]() |
![]() |
![]() |
![]() |
詩集詳情 | 做者列表 | 做者詳情 | 字體選擇 |
![]() |
![]() |
![]() |
![]() |
和Django快速開發實踐的文章同樣,本文不講廢話,直接上步驟。微信
先設計好項目文件結構,不一樣的項目有不一樣的需求,按照本身的實際須要來設計結構就行了,如下是個人項目結構,僅供參考:網絡
lib
├── common
├── i10n
├── models
├── routes
├── states
└── widgets
複製代碼
文件夾 | 做用 |
---|---|
common | 一些工具類,如通用方法類、網絡接口類、保存全局變量的靜態類等 |
i10n | 存放國際化相關代碼 |
models | 經過json to models生成的model類文件都存在這裏 |
routes | 存放項目的全部頁面代碼 |
states | 保存app中須要跨組件共享的狀態類 |
widgets | 存放自定義widget |
在本項目中,我使用json to models
來自動生成models類,爲何使用這個呢?緣由很簡單,減小工做量,用json定義好app中使用到的模型,生成model類以後能夠很方便序列化成json數據進行持久化和或者從配置文件中讀取json數據反序列化成model對象,還能夠直接根據後臺接口返回的json數據生成model類,很是方便。app
使用json定義model,例子以下:less
在項目根目錄下建立json文件夾,添加要進行轉換的json文件,內容大概像這樣。 poem.jsonide
{
"strains": [
"平平平仄仄,平仄仄平平。",
"仄仄平平仄,平平仄仄平。",
"平平平仄仄,平仄仄平平。",
"平仄仄平仄,平平仄仄平。"
],
"author": "做者名稱",
"authorObj": "$author",
"paragraphs": [
"秦川雄帝宅,函谷壯皇居。",
"綺殿千尋起,離宮百雉餘。",
"連甍遙接漢,飛觀迥凌虛。",
"雲日隱層闕,風煙出綺疎。"
],
"tags": [
"戰爭",
"生活",
"冬天",
"愛國",
"邊塞"
],
"chapter": "國風",
"section": "周南",
"rhythmic": "玉樓春",
"title": "帝京篇十首 一",
"content": "經傳宜獨坐讀,史鑑宜與友共讀。",
"comment": [
"孫愷似曰:深得此中真趣,固難爲不知者道。",
"王景州曰:如無好友,即紅友亦可。"
],
"notes": [
"1.小山--寫女子的隔夜殘妝。小山:女子畫眉的式樣之一。小山重疊:眉暈褪色。金:額黃,在額上塗黃色。金明滅:褪色的額黃明暗不勻。",
"2.鬢雲欲度--鬢髮撩亂如雲,低垂下來。香腮雪:潔白如雪的香腮。",
"3.照花--對鏡簪花。用前鏡、後鏡對照以瞻顧後影。",
"4.雙雙--羅襦上用金線繡的成雙的鷓鴣鳥。反襯自身孤獨。"
],
"anthology": "所屬詩集名稱",
"id": "08e41396-2809-423d-9bbc-1e6fb24c0ca1"
}
複製代碼
添加依賴:工具
dev_dependencies:
json_model: ^0.0.2
build_runner: ^1.0.0
json_serializable: ^2.0.0
複製代碼
好了以後運行:字體
flutter packages pub run json_model
複製代碼
這樣就會自動在lib/models
文件夾下面生成models類啦。
有個坑爹的地方是這個json_model
庫只能支持很老版本的build_runner
和json_serializable
,這和我後面要用到的intl
就衝突了啊,每次用這兩個庫的時候我都要不斷註釋切換依賴的版本,真的麻煩 = =....
狀態管理是app中最重要的一部分,也是後面主題切換和國際化的基礎。 本文是快速開發實踐,不過多深刻Flutter的狀態管理,想了解的同窗能夠看看大佬寫的Flutter教程:book.flutterchina.club/chapter7/pr…
我是使用Provider
這個組件來管理app的狀態的,它基於InheritedWidget
實現,用起來挺方便。
先添加依賴:
dependencies:
provider: ^3.2.0
複製代碼
在lib/states
文件夾下添加共享狀態的models,例如:
import 'package:flutter/material.dart';
import 'package:minimal_poem/common/global.dart';
import 'package:minimal_poem/models/index.dart';
import 'notifier.dart';
class ProfileChangeNotifier extends ChangeNotifier {
Profile get profile => Global.profile;
@override
void notifyListeners() {
// 保存Profile變動
Global.saveProfile();
Global.saveAllUsers();
super.notifyListeners(); // 通知依賴的Widget更新
}
}
class ThemeModel extends ProfileChangeNotifier {
// 獲取當前主題,若是未設置主題,則默認使用藍色主題
ColorSwatch get theme => Global.themes.firstWhere((e) => e.value == profile.theme, orElse: () => Colors.blue);
// 主題改變後,通知其依賴項,新主題會當即生效
set theme(ColorSwatch color) {
if (color != theme) {
profile.theme = color[500].value;
notifyListeners();
}
}
bool get darkMode => Global.profile.darkMode;
set darkMode(bool value) {
Global.profile.darkMode = value;
notifyListeners();
}
}
複製代碼
這些model繼承自ProfileChangeNotifier
,能夠提供數據或者管理數據的修改和保存。
在普通的組件裏能夠直接使用獲取或保存數據,配合provider
組件使用能夠在model數據改變的時候出發組件的更新動做~
例如個人MyApp
類定義,用到了MultiProvider
和Consumer2
:
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: <SingleChildCloneableWidget>[
ChangeNotifierProvider.value(value: ThemeModel()),
ChangeNotifierProvider.value(value: UserModel()),
ChangeNotifierProvider.value(value: LocaleModel()),
],
child: Consumer2<ThemeModel, LocaleModel>(
builder: (BuildContext context, themeModel, localeModel, Widget child) {
return MaterialApp(
theme: ThemeData(
brightness: Global.profile.darkMode ? Brightness.dark : Brightness.light,
primarySwatch: themeModel.theme,
),
onGenerateTitle: (context) {
return DaLocalizations.of(context).title;
},
home: HomeRoute(),
//應用主頁
locale: localeModel.getLocale(),
//咱們只支持美國英語和中文簡體
supportedLocales: [
const Locale('zh', 'CN'), // 中文簡體
const Locale('en', 'US'), // 美國英語
//其它Locales
],
localizationsDelegates: [
// 本地化的代理類
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
// EasyRefresh的多語言支持
GlobalEasyRefreshLocalizations.delegate,
// 註冊咱們的Delegate
DaLocalizationsDelegate()
],
localeResolutionCallback: (Locale _locale, Iterable<Locale> supportedLocales) {
if (localeModel.getLocale() != null) {
//若是已經選定語言,則不跟隨系統
return localeModel.getLocale();
} else {
Locale locale;
// APP語言跟隨系統語言,若是系統語言不是中文簡體或美國英語,
// 則默認使用美國英語
if (supportedLocales.contains(_locale)) {
locale = _locale;
} else {
locale = Locale('en', 'US');
}
return locale;
}
},
);
},
),
);
}
}
複製代碼
國際化就是多語言啦,用到了intl
包。
在項目根目錄下建立文件夾i10n-arb
,在lib/i10n
裏建立localization_intl.dart
:
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'messages_all.dart';
class DaLocalizations {
String get userNameOrPasswordWrong => null;
static Future<DaLocalizations> load(Locale locale) {
final String name = locale.countryCode.isEmpty ? locale.languageCode : locale.toString();
final String localeName = Intl.canonicalizedLocale(name);
//2
return initializeMessages(localeName).then((b) {
Intl.defaultLocale = localeName;
return new DaLocalizations();
});
}
static DaLocalizations of(BuildContext context) {
return Localizations.of<DaLocalizations>(context, DaLocalizations);
}
String get auto => Intl.message('auto', name: 'auto', desc: 'set theme mode auto');
}
//Locale代理類
class DaLocalizationsDelegate extends LocalizationsDelegate<DaLocalizations> {
const DaLocalizationsDelegate();
//是否支持某個Local
@override
bool isSupported(Locale locale) => ['en', 'zh'].contains(locale.languageCode);
// Flutter會調用此類加載相應的Locale資源類
@override
Future<DaLocalizations> load(Locale locale) {
//3
return DaLocalizations.load(locale);
}
// 當Localizations Widget從新build時,是否調用load從新加載Locale資源.
@override
bool shouldReload(DaLocalizationsDelegate old) => false;
}
複製代碼
運行命令生成arb文件:
flutter pub pub run intl_translation:extract_to_arb --output-dir=i10n-arb lib/i10n/localization_intl.dart
複製代碼
以後會在i10n-arb
文件夾下生成intl_messages.arb
文件,這個本質上是一個json文件,咱們還要爲不一樣的語言版本建立對應的翻譯,好比本app支持中文和英文,那麼須要建立兩個文件:intl_zh.arb
和intl_en.arb
。
把intl_messages.arb
文件的內容分別複製到對應語言的翻譯文件中,修改爲對應語言的版本便可。
{
"@@last_modified": "2019-12-17T17:04:43.001945",
"title": "title",
"@title": {
"type": "text",
"placeholders": {}
},
}
複製代碼
上面這些作完以後運行命令生成對應的類:
# 從arb文件生成dart代碼
flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/i10n --no-use-deferred-loading lib/i10n/localization_intl.dart i10n-arb/intl_*.arb
複製代碼
原來有這麼多內容,限制於篇幅,我將在接下來的文章中繼續記錄~
交流問題請在微信公衆號後臺留言,每一條信息我都會回覆哈~