前言
- 中文文档网站 (flutter.cn/)
- 包管理 (pub.flutter-io.cn/)
- 项目使用到的插件
provider-- 状态管理dio-- 网络请求flustars-- 常用工具类库(目前包含SharedPreferences Util, Screen Util, Directory Util, Widget Util, Image Util)
一、新建项目
1. 新建
参考文档中对 Android Studio and Intellij、Visual Studio Code 对应的新建方式。
2. 目录
Project
├─ .dart_tool 记录了一些dart工具库所在的位置和信息
├─ .idea android studio 是基于idea开发的,.idea 记录了项目的一些文件的变更记录
├─ android Android项目文件夹
├─ build 编译或运行后产物
├─ ios iOS项目文件夹
├─ lib 包含.dart结尾的工程相关文件
│ ├─ http
│ ├─ layout 布局
│ │ └─ basicLayout.dart
│ ├─ models
│ ├─ pages 页面
│ ├─ themes 主题
│ ├─ utils 工具
│ ├─ widgets 公共组件
│ ├─ main.dart 入口文件
│ └─ routes.dart 路由
├─ test 用于存放我们的测试代码
├─ .gitignore git忽略配置文件
├─ .metadata IDE 用来记录某个 Flutter 项目属性的的隐藏文件
├─ .packages pub 工具需要使用的,包含 package 依赖的 yaml 格式的文件
├─ flutter_app.iml 工程文件的本地路径配置
├─ pubspec.lock 当前项目依赖所生成的文件
├─ pubspec.yaml 当前项目的一些配置文件,包括依赖的第三方库、图片资源文件等
└─ README.md READEME文件
二、基础布局
1. 创建相关页面
pages/home/index.dart
import 'package:flutter/material.dart';
class Home extends StatefulWidget{
@override
_Home createState()=> new _Home();
}
class _Home extends State<Home>{
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Home'),
),
body: new Container(
child: new Column(
children: <Widget>[
new Text('Home'),
new Text('Home'),
],
),
),
);
}
}
pages/my/index.dart
import 'package:flutter/material.dart';
class My extends StatefulWidget{
@override
_My createState()=> new _My();
}
class _My extends State<My>{
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('My'),
),
body: new Container(
child: new Column(
children: <Widget>[
new Text('My'),
new Text('My'),
],
),
),
);
}
}
2. 创建基础布局页面(bottomNavigationBar 底部导航栏)
layout/basicLayout.dart
import 'package:flutter/material.dart';
import 'package:flutter_template/pages/home/index.dart';
import 'package:flutter_template/pages/my/index.dart';
// 这个页面是作为整个APP的最外层的容器,以Tab为基础控制每个item的显示与隐藏
class BasicLayout extends StatefulWidget {
@override
_BasicLayout createState() => new _BasicLayout();
}
class _BasicLayout extends State<BasicLayout> {
// bottomNavigationBar 当前的 val 值
int _selectedIndex = 0;
// pages
List<Widget> pages = [Home(),My()];
// bottomNavigationBar 切换
void _onTabChanged(value) {
setState(() {
// print('Tab -- $value');
_selectedIndex = value;
});
}
@override
Widget build(BuildContext context) {
return new Scaffold(
bottomNavigationBar: new BottomNavigationBar(
type: BottomNavigationBarType.fixed,
// backgroundColor: Color.fromRGBO(214,211,205, 1),
// selectedItemColor:Colors.red,
// unselectedItemColor: Colors.red,
selectedFontSize: 14,
unselectedFontSize: 14,
currentIndex: _selectedIndex,
onTap: (value) => {
_onTabChanged(value)
// Respond to item press.
},
items: [
BottomNavigationBarItem(title: Text('Home'), icon: Icon(Icons.radio_button_unchecked)),
BottomNavigationBarItem(
title: Text('My'), icon: Icon(Icons.details)),
],
),
body: pages[_selectedIndex],
);
}
}
3. 将基础布局页面放置 MaterialApp 里面
main.dart
import 'package:flutter/material.dart';
import './routes.dart';
import 'package:flutter_template/layout/basicLayout.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter template',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home:BasicLayout(), // <= 这里
);
}
}
三、路由
1. 创建相关页面
pages/demo/index.dart
import 'package:flutter/material.dart';
class Demo extends StatefulWidget{
@override
_Demo createState()=> new _Demo();
}
class _Demo extends State<Demo>{
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Demo'),
),
body: new Container(
child: new Column(
children: <Widget>[
new Text('Demo'),
new Text('Demo'),
],
),
),
);
}
}
2. 配置路由
routes.dart
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_template/pages/home/index.dart';
import 'package:flutter_template/pages/my/index.dart';
import 'package:flutter_template/pages/demo/index.dart';
class RouteConfig {
static final initRouteName = '/my'; // 默认路由
static final Map<String, WidgetBuilder> router = {
// '/basicLayout': (BuildContext context) => BasicLayout(), // 不带参路由
// '/basicLayout':(BuildContext context,{arguments})=> BasicLayout(arguments:arguments), // 带参路由
'/home': (BuildContext context) => Home(),
'/my': (BuildContext context) => My(),
'/demo':(BuildContext context) => Demo(),
};
static Route<dynamic> onGenerateRoute(RouteSettings settings) {
// 统一处理路由
final String name = settings.name;
final Function pageContentBuilder = router[name];
print('routeNameSettings ------ $settings');
// 定义当前需要返回的 route 对象
Route route;
if (pageContentBuilder != null) {
if (settings.arguments != null) {
// 带参数的处理方式
switch (name) {
default:
route = CupertinoPageRoute(
builder: (context) =>
pageContentBuilder(context, arguments: settings.arguments));
break;
}
} else {
// 不带参数的处理方式
switch (name) {
case '/signUp':
route = CupertinoPageRoute(
builder: (context) => pageContentBuilder(context),
fullscreenDialog:
true // fullscreenDialog表示新的路由页面是否是一个全屏的模态对话框,在iOS中,如果fullscreenDialog为true,新页面将会从屏幕底部滑入(而不是水平方向)。
);
break;
default:
//这里调用自定义页面切换为淡入淡出
// route = FadeAnimation(pageContentBuilder);
route = CupertinoPageRoute(
builder: (context) => pageContentBuilder(context));
break;
break;
}
}
}
return route;
}
}
3. 将路由文件配置到 main.dart
main.dart
import 'package:flutter/material.dart';
import './routes.dart';
import 'package:flutter_template/layout/basicLayout.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter template',
// initialRoute: RouteConfig.initRouteName, // 初始路由
onGenerateRoute: RouteConfig.onGenerateRoute, // 路由配置
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home:BasicLayout(),
);
}
}
跳转路由
new FlatButton(
color: Colors.blue,
highlightColor: Colors.redAccent[700],
splashColor: Colors.grey,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
child: Text('跳转到Demo页面',style: TextStyle(color: Colors.white),),
onPressed: () {
Navigator.pushNamed(context, '/demo'); // <= 路由跳转
},
)
路由小知识 juejin.cn/post/684490…
四. 暗黑模式
1. 小前言
我们可以直接在 MaterialApp 的 darkTheme 选项中使用:
MaterialApp(
theme: ThemeData(
brightness: Brightness.light,
primaryColor: Colors.blue,
),
darkTheme: ThemeData(
brightness: Brightness.dark,
),
);
也可以写成:
darkTheme: ThemeData.dark()
这样写的好处是,用户无需单独设置深/浅色模式,完全根据系统设置来切换。
但是,
我们更多的是需要有控制权(手动)来设置。
2. 准备
这里需要使用 shared_preferences 保存用户设置,通过 Provider 实现状态管理。
flustars 号称 ”Flutter 全网最全常用工具类”,这里使用 SpUtil 来储存用户所选择的信息;
Provider 官方推荐的状态管理库,相较其他库来说更具优势;
provider: ^4.3.2+2
flustars: ^0.3.3
3. 配置全局的主题
themes/themes.dart
// 由于直接在 main.dart 配置 明亮/暗黑 样式会显得很乱,故提出来单独处理
import 'package:flutter/material.dart';
class Themes {
// 明亮
static ThemeData lightTheme() {
return ThemeData(
brightness: Brightness.light,
// appBarTheme: AppBarTheme(
// color: Color.fromRGBO(229, 229, 229, 1),
// elevation: 0, //去掉Appbar底部阴影
// iconTheme: IconThemeData(
// color: Color(0xFF323232), //change your color here
// ),
// ),
// scaffoldBackgroundColor: Color.fromRGBO(229, 229, 229, 1),
);
}
// 暗黑
static ThemeData dartTheme() {
return ThemeData(
brightness: Brightness.dark,
// appBarTheme: AppBarTheme(
// color: Colors.black,
// elevation: 0, //去掉Appbar底部阴影
// iconTheme: IconThemeData(
// color: Color(0xFFFFFFFF), //change your color here
// ),
// ),
// scaffoldBackgroundColor: Colors.black,
);
}
}
其他 ThemeData 的参数:
4. 主题模式状态管理类
models/globalModeProvider.dart
import 'package:flutter/material.dart';
import 'package:flustars/flustars.dart';
import 'package:flutter_template/themes/themes.dart';
class GlobalModeProvider with ChangeNotifier {
// 主题 0:浅色 1: 深色 2: 随系统
int _darkMode = SpUtil.getInt("darkMode", defValue: 2); // defValue 默认值
int get darkMode => _darkMode;
ThemeData get themeValue => darkMode == 2
? Themes.lightTheme()
: darkMode == 1 ? Themes.dartTheme() : Themes.lightTheme(); // 是否是随系统?是的就是明亮,不是,则按照手动选择主题展示
ThemeData get dartThemeValue => darkMode == 2 ? Themes.dartTheme() : null; // 是否是随系统?是的就是暗黑,不是,为空
// 更改主题
void changeThemeMode(int darkMode) async {
_darkMode = darkMode;
// print('当前主题$_darkMode');
notifyListeners(); // `notifyListeners();` 用于通知顶层容器状态的变化
SpUtil.putInt('darkMode', darkMode); // 用于保存用户设置(持久化选择)。
}
}
5. 配置 main.dart
import 'package:flutter/material.dart';
import 'package:flustars/flustars.dart';
import 'package:flutter/rendering.dart';
import 'package:provider/provider.dart';
import './routes.dart';
import 'package:flutter_template/models/globalModeProvider.dart';
import 'package:flutter_template/layout/basicLayout.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized(); // 在 runApp 之前若需要初始化插件,则需要此行代码
await SpUtil.getInstance(); // 初始化 SpUtil (由于 SharedPreferences 需要异步生成才能使用。)
debugPaintSizeEnabled = !true;
runApp(
// 全局注册 model
MultiProvider(providers: [
ChangeNotifierProvider.value(value: GlobalModeProvider()),
], child: MyApp()),
);
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return Consumer<GlobalModeProvider>(
builder: (context, globalModeProvider, _) {
return MaterialApp(
title: 'Flutter template',
initialRoute: RouteConfig.initRouteName, // 初始路由
onGenerateRoute: RouteConfig.onGenerateRoute, // 路由配置
theme: globalModeProvider.themeValue,
darkTheme: globalModeProvider.dartThemeValue,
home: BasicLayout(),
);
});
}
}
6. 创建进入设置页面入口
pages/my/index.dart
body: new Container(
child: new Column(
children: <Widget>[
new Container(
width: MediaQuery.of(context).size.width, // 获取屏幕的宽度
height: 55,
child: new FlatButton(
onPressed: () {
Navigator.pushNamed(context, '/setting');
},
child: new Flex(
direction: Axis.horizontal,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Row(
children: <Widget>[
new Icon(Icons.settings,size: 20),
new Padding(padding: EdgeInsets.only(right: 10)),
new Text('设置',),
],
),
new Icon(Icons.chevron_right),
],
),
))
],
),
),
7. 设置页面
pages/setting/index.dart
import 'package:flutter/material.dart';
import 'package:flutter_template/models/globalModeProvider.dart';
import 'package:provider/provider.dart';
class Setting extends StatefulWidget {
@override
_Setting createState() => new _Setting();
}
class _Setting extends State<Setting> {
// 更改 主题
void _changeTheme(context, value) {
Provider.of<GlobalModeProvider>(context, listen: false)
.changeThemeMode(value);
}
// 渲染主题列表
List<Widget> renderthemeLists(context, themeLists, _currentTheme) {
List<Widget> widgetTheme = [];
for (int i = 0; i < themeLists.length; i++) {
widgetTheme.add(
new Container(
width: MediaQuery.of(context).size.width,
height: 55,
decoration: BoxDecoration(
border: Border(
top: BorderSide(
color: Colors.black12,
width: 1,
style: BorderStyle.solid))),
child: new FlatButton(
onPressed: () => _changeTheme(context, themeLists[i]['value']),
child: new Container(
padding: EdgeInsets.only(left: 0, right: 0),
child: new Flex(
direction: Axis.horizontal,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Text(themeLists[i]['label']),
_currentTheme == themeLists[i]['value']
? new Icon(Icons.done)
: new Text('')
],
),
),
)),
);
}
return widgetTheme;
}
@override
Widget build(BuildContext context) {
List themeLists = [
{'label': '明亮模式', 'value': 0},
{'label': '暗黑模式', 'value': 1},
{'label': '跟随系统', 'value': 2}
];
// 仓库
final _globalModeStore = Provider.of<GlobalModeProvider>(context);
// 当前主题
var _currentTheme = _globalModeStore.themeMode;
return new Scaffold(
appBar: new AppBar(
title: new Text('设置'),
),
body: new Container(
child: new Column(
children: <Widget>[
new ExpansionTile(
title: new Text('主题'),
children: renderthemeLists(context, themeLists, _currentTheme))
],
),
),
);
}
}
五、暗黑模式扩展
如果需要自定义组件的颜色样式等等,这有一个参考的解决方案。
问题?
如图,无论在明亮还是暗黑模式下,块A是蓝色,块B是红色。但我们需求是明亮模式下块A是蓝色,块B是红色,暗黑模式下块A是黄色色,块B是绿色。
pages/demo/index.dart
import 'package:flutter/material.dart';
class Demo extends StatefulWidget {
@override
_Demo createState() => new _Demo();
}
class _Demo extends State<Demo> {
@override
Widget build(BuildContext context) {
final gW = MediaQuery.of(context).size.width;
return new Scaffold(
appBar: new AppBar(
title: new Text('Demo'),
),
body: new Container(
padding: EdgeInsets.all(10),
child: new Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Container(
width: gW,
height: 150,
decoration: BoxDecoration(
color: Colors.blue,
),
),
new Container(
width: gW,
height: 150,
margin: EdgeInsets.only(top: 10),
decoration: BoxDecoration(
color: Colors.red,
),
)
],
),
),
);
}
}
解决方案!
themes/colorsThemes.dart (colors 配置表)
import 'package:flutter/material.dart';
class ColorsThemes{
static dynamic theme(context) {
final themeType = Theme.of(context).brightness.toString().toLowerCase(); // 获取当前主题
var lightTheme = {
'demoBY': Colors.blue,
'demoRG':Colors.red
};
var dartTheme = {
'demoBY': Colors.yellow,
'demoRG':Colors.green
};
if (themeType == 'brightness.dark') {
// brightness.dark 暗黑主题
return dartTheme;
} else {
// brightness.light 亮色主题
return lightTheme;
}
}
}
pages/demo/index.dart
import 'package:flutter/material.dart';
import 'package:flutter_template/themes/colorsThemes.dart'; // 引入颜色配置表
class Demo extends StatefulWidget {
@override
_Demo createState() => new _Demo();
}
class _Demo extends State<Demo> {
@override
Widget build(BuildContext context) {
final gW = MediaQuery.of(context).size.width;
final colors = ColorsThemes.theme(context); // 定义
return new Scaffold(
appBar: new AppBar(
title: new Text('Demo'),
),
body: new Container(
padding: EdgeInsets.all(10),
child: new Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Container(
width: gW,
height: 150,
decoration: BoxDecoration(
color: colors['demoBY'], // 使用
),
),
new Container(
width: gW,
height: 150,
margin: EdgeInsets.only(top: 10),
decoration: BoxDecoration(
color: colors['demoRG'], // 使用
),
)
],
),
),
);
}
}
六、国际化
1. 小前言
网上解决国际化的方案有很多,找到的这种 Flutter intl 插件形式相对比较简单;
2. 准备
Flutter intl 插件介绍:
- juejin.cn/post/684490… ;
- vs code 同理;
3. 配置多语言
l18n/intl_en.arb
{
"Home": "Home",
"jumpDemoPage": "Skip to the Demo page",
"Demo": "Demo",
"My": "My",
"Setting": "Setting",
"multiLanguage": "multi-language",
"FollowSystem": "Follow the system",
"Theme": "Theme",
"LightMode": "Light mode",
"DarkMode": "Dark mode"
}
intl_zh_CN.arb
{
"Home": "首页",
"jumpDemoPage": "跳转到{page}页面",
"Demo": "示例",
"My": "我的",
"Setting": "设置",
"multiLanguage": "多语言",
"FollowSystem": "跟随系统",
"Theme": "主题",
"LightMode": "明亮模式",
"DarkMode": "暗黑模式"
}
intl_zh_HK.arb
{
"Home": "首頁",
"jumpDemoPage": "跳轉到{page}頁面",
"Demo": "示例",
"My": "我的",
"Setting": "設置",
"multiLanguage": "多語言",
"FollowSystem": "跟隨系統",
"Theme": "主題",
"LightMode": "明亮模式",
"DarkMode": "暗黑模式"
}
……(更多的其他语言)
4. 配置 main.dart
import 'package:flutter/material.dart';
import 'package:flustars/flustars.dart';
import 'package:flutter/rendering.dart';
import 'package:provider/provider.dart';
import 'package:flutter_template/generated/l10n.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import './routes.dart';
import 'package:flutter_template/models/globalModeProvider.dart';
import 'package:flutter_template/layout/basicLayout.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized(); // 在 runApp 之前若需要初始化插件,则需要此行代码
await SpUtil.getInstance(); // 初始化 SpUtil (由于 SharedPreferences 需要异步生成才能使用。)
debugPaintSizeEnabled = !true;
runApp(
// 全局注册 model
MultiProvider(providers: [
ChangeNotifierProvider.value(value: GlobalModeProvider()),
], child: MyApp()),
);
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return Consumer<GlobalModeProvider>(
builder: (context, globalModeProvider, _) {
return MaterialApp(
title: 'Flutter template',
// initialRoute: RouteConfig.initRouteName, // 初始路由
onGenerateRoute: RouteConfig.onGenerateRoute, // 路由配置
theme: globalModeProvider.themeValue,
darkTheme: globalModeProvider.dartThemeValue,
// 默认
locale: Locale('zh','CN'), // <= 可以更改这里看到效果
// 设置语言
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate, // 指定本地化的字符串和一些其他的值
GlobalCupertinoLocalizations.delegate, // 对应的Cupertino风格
GlobalWidgetsLocalizations.delegate // 指定默认的文本排列方向, 由左到右或由右到左
],
// 讲en设置为第一项,没有适配语言时,英语为首选项
supportedLocales: [
const Locale('en', ''),
const Locale('zh', 'CN'),
...S.delegate.supportedLocales
],
// // 插件目前不完善手动处理简繁体
localeResolutionCallback: (locale, supportLocales) {
print('当前系统语言环境$locale');
// 中文 简繁体处理
// if (locale?.languageCode == 'zh') {
// if (locale?.scriptCode == 'Hant') {
// return const Locale('zh', 'HK'); //繁体
// } else {
// return const Locale('zh', 'CN'); //简体
// }
// }
return null;
},
home: BasicLayout(),
);
});
}
}
在需要使用的文件中:
import 'package:flutter_template/generated/l10n.dart';
// 两种方式使用
new Text(S.current.Home),
new Text(S.of(context).Home),
5. 手动修改语言
这里跟修改主题是一样的,也需要使用 shared_preferences 保存用户设置,通过 Provider 实现状态管理。
5.1 配置全局的语言
models/globalModeProvider.dart
import 'package:flutter/material.dart';
import 'package:flustars/flustars.dart';
import 'package:flutter_template/themes/themes.dart';
class GlobalModeProvider with ChangeNotifier {
// 主题 0:浅色 1: 深色 2: 随系统
int _themeMode = SpUtil.getInt("themeMode", defValue: 2); // defValue 默认值
int get themeMode => _themeMode;
ThemeData get themeValue => themeMode == 2
? Themes.lightTheme()
: themeMode == 1 ? Themes.dartTheme() : Themes.lightTheme(); // 是否是随系统?是的就是明亮,不是,则按照手动选择主题展示
ThemeData get dartThemeValue => themeMode == 2 ? Themes.dartTheme() : null; // 是否是随系统?是的就是暗黑,不是,为空
// 语言
dynamic _languageMode = SpUtil.getString("languageMode", defValue: null); // null 为跟随系统
String get languageMode => _languageMode;
Locale get localeValue => _languageMode == null ? null: Locale(_languageMode.split('_')[0], _languageMode.split('_')[1]);
// 更改主题
void changeThemeMode(int themeMode) async {
_themeMode = themeMode;
// print('当前主题$_darkMode');
notifyListeners(); // `notifyListeners();` 用于通知顶层容器状态的变化
SpUtil.putInt('themeMode', themeMode); // 用于保存用户设置。
}
// 更改语言
void changeLanguageMode(String languageMode) async {
_languageMode = languageMode;
// print('当前语言 $languageMode');
notifyListeners();
SpUtil.putString('languageMode', languageMode);
}
}
5.2 配置 main.dart
main.dart
// 更改默认
// locale: Locale('zh','CN'), // <= 可以更改这里看到效果
locale: globalModeProvider.localeValue,
5.3 设置页面
pages/setting/index.dart
import 'package:flutter/material.dart';
import 'package:flutter_template/models/globalModeProvider.dart';
import 'package:provider/provider.dart';
import 'package:flutter_template/generated/l10n.dart';
class Setting extends StatefulWidget {
@override
_Setting createState() => new _Setting();
}
class _Setting extends State<Setting> {
// 更改 语言
void _changeLanguage(context, value) async {
Provider.of<GlobalModeProvider>(context, listen: false)
.changeLanguageMode(value);
}
// 更改 主题
void _changeTheme(context, value) {
Provider.of<GlobalModeProvider>(context, listen: false)
.changeThemeMode(value);
}
// 渲染语言列表
List<Widget> renderLangugeLists(context, langugeLists, _currentLanguge) {
List<Widget> widgetLanguge = [];
langugeLists.forEach((item) => {
widgetLanguge.add(
new Container(
width: MediaQuery.of(context).size.width,
height: 55,
decoration: BoxDecoration(
border: Border(
top: BorderSide(
color: Colors.black12,
width: 1,
style: BorderStyle.solid))),
child: new FlatButton(
onPressed: () => _changeLanguage(context, item['value']),
child: new Container(
padding: EdgeInsets.only(left: 0, right: 0),
child: new Flex(
direction: Axis.horizontal,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Text(item['label']),
_currentLanguge == item['value']
? new Icon(Icons.done)
: new Text('')
],
),
),
)),
)
});
return widgetLanguge;
}
// 渲染主题列表
List<Widget> renderthemeLists(context, themeLists, _currentTheme) {
List<Widget> widgetTheme = [];
for (int i = 0; i < themeLists.length; i++) {
widgetTheme.add(
new Container(
width: MediaQuery.of(context).size.width,
height: 55,
decoration: BoxDecoration(
border: Border(
top: BorderSide(
color: Colors.black12,
width: 1,
style: BorderStyle.solid))),
child: new FlatButton(
onPressed: () => _changeTheme(context, themeLists[i]['value']),
child: new Container(
padding: EdgeInsets.only(left: 0, right: 0),
child: new Flex(
direction: Axis.horizontal,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Text(themeLists[i]['label']),
_currentTheme == themeLists[i]['value']
? new Icon(Icons.done)
: new Text('')
],
),
),
)),
);
}
return widgetTheme;
}
@override
Widget build(BuildContext context) {
List langugeLists = [
{'label': S.current.FollowSystem, 'value': null},
{'label': 'English', 'value': 'en_US'},
{'label': '简体中文', 'value': 'zh_CN'},
{'label': '繁體中文', 'value': 'zh_HK'},
];
List themeLists = [
{'label': S.current.FollowSystem, 'value': 2},
{'label': S.current.LightMode, 'value': 0},
{'label': S.current.DarkMode, 'value': 1},
];
// 仓库
final _globalModeStore = Provider.of<GlobalModeProvider>(context);
// 当前主题
var _currentTheme = _globalModeStore.themeMode;
// 当前语言
var _currentLanguge = _globalModeStore.languageMode;
final intls = S.of(context);
return new Scaffold(
appBar: new AppBar(
title: new Text(intls.Setting),
),
body: new Container(
child: new Column(
children: <Widget>[
new ExpansionTile(
title: new Text(intls.multiLanguage),
children:
renderLangugeLists(context, langugeLists, _currentLanguge),
),
new ExpansionTile(
title: new Text(intls.Theme),
children: renderthemeLists(context, themeLists, _currentTheme))
],
),
),
);
}
}
7、Http 请求
1. 安装 dio
dependencies:
dio: ^3.0.10
2. 配置 dio
pages/HttpUtils.dart
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
// 原始请求方式
// void getHttp() async {
// try {
// Response response = await Dio().get("http://81.69.13.123:7001/api/getUsersTestDataAll");
// print(response);
// } catch (e) {
// print(e);
// }
// }
class DioUtils {
static const String BASE_URL = "http://81.69.13.123:7001/api"; //base url
static DioUtils _instance;
Dio _dio;
BaseOptions _baseOptions;
static DioUtils getInstance() {
if (_instance == null) {
_instance = new DioUtils();
}
return _instance;
}
// dio初始化配置
DioUtils() {
//请求参数配置
_baseOptions = new BaseOptions(
baseUrl: BASE_URL,
connectTimeout: 5000,
receiveTimeout: 5000,
headers: {
//预设好的header信息 需要配置请求的header可在此处配置
"testHeader": "bb"
},
//请求的Content-Type,默认值是[ContentType.json]. 也可以用ContentType.parse("application/x-www-form-urlencoded")
contentType: "application/x-www-form-urlencoded",
//表示期望以那种格式(方式)接受响应数据。接受三种类型 `json`, `stream`, `plain`, `bytes`. 默认值是 `json`,
responseType: ResponseType.json,
);
//创建dio实例
_dio = new Dio(_baseOptions);
//可根据项目需要选择性的添加请求拦截器
_dio.interceptors.add(
InterceptorsWrapper(onRequest: (RequestOptions requestions) async {
//此处可网络请求之前做相关配置,比如会所有请求添加token,或者userId
// requestions.queryParameters["token"] = "testtoken123443423";
// requestions.queryParameters["userId"] = "123456";
// print('-----请求参数--' + requestions.queryParameters.toString());
return requestions;
}, onResponse: (Response response) {
//此处拦截工作在数据返回之后,可在此对dio请求的数据做二次封装或者转实体类等相关操作
// print('------ $response');
return response;
}, onError: (DioError error) {
//处理错误请求
return error;
}),
);
}
// get请求
get(url, {data, options}) async {
print('get request path ------$url-------请求参数--$data');
Response response;
try {
response = await _dio.get(url, queryParameters: data, options: options);
debugPrint('get result ---${response.data}');
} on DioError catch (e) {
print('请求失败---错误类型${e.type}--错误信息${e.message}');
}
return response.data;
}
// Post请求
post(url, {data, options}) async {
print('post request path ------$url-------请求参数$data');
Response response;
try {
response = await _dio.post(url, queryParameters: data, options: options);
print('post result ---${response.data}');
} on DioError catch (e) {
print('请求失败---错误类型${e.type}--错误信息${e.message}');
}
return response.data;
}
}
3. 配置接口路径
pages/apiUrls.dart
// 接口
class ApiUrls {
static const String BASE_URL = 'http://81.69.13.123:7001/api';
// 全部用户数据
static String getUsersTestDataAllApi() {
return '/getUsersTestDataAll';
}
}
4. 配置接口
pages/https.dart
import './HttpUtils.dart';
import './apiUrls.dart';
class Https {
// 获取全部用户数据
static getUsersTestDataAll() async {
Map<String, dynamic> result =
await DioUtils().get(ApiUrls.getUsersTestDataAllApi());
return result;
}
}
5. 页面请求
pages/home/index.dart
import 'package:flutter/material.dart';
import 'package:flutter_template/generated/l10n.dart';
import 'package:flutter_template/http/https.dart';
class Home extends StatefulWidget {
@override
_Home createState() => new _Home();
}
class _Home extends State<Home> {
String requestData = 'null'; // 局部 state
@override
Widget build(BuildContext context) {
final intls = S.of(context);
// 请求方法
onGetUsersTestDataAll() {
Https.getUsersTestDataAll().then((res) {
// setState 更改 state
setState(() {
requestData = res['data'].toString();
});
});
}
print(requestData);
return new Scaffold(
appBar: new AppBar(
title: new Text(intls.Home),
),
body: new Center(
child: new Column(
children: <Widget>[
new FlatButton(
color: Colors.blue,
highlightColor: Colors.redAccent[700],
splashColor: Colors.grey,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0)),
child: Text(
intls.jumpDemoPage('Demo'),
style: TextStyle(color: Colors.white),
),
onPressed: () {
Navigator.pushNamed(context, '/demo');
},
),
new FlatButton(
color: Colors.blue,
highlightColor: Colors.redAccent[700],
splashColor: Colors.grey,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0)),
child: Text(
intls.mockRequest,
style: TextStyle(color: Colors.white),
),
onPressed: onGetUsersTestDataAll,
),
new Container(
width: 200,
height: 200,
decoration: BoxDecoration(border: Border.all(width: 0.5)),
child: new Text(requestData))
],
),
),
);
}
}
后言
以上均是此时实践下来的总结,只是提供一种整套的解决方案,不一定最佳,且 flutter 目前还不太成熟,变化快。望各位多多指教,提供更佳的解决方案,再修改完善。🤣🤣🤣
项目地址 (github.com/lEnGKX/flut…)