日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Flutter基础笔记

發(fā)布時(shí)間:2025/3/19 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Flutter基础笔记 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄

  • List里面常用的屬性和方法:
  • Set
  • Map
  • forEach,map, where,any,every
  • extends抽象類 和 implements
  • Flutter環(huán)境搭建
  • 入口文件、入口方法
  • 第一個(gè) Demo Center 組件的 使用
  • 把內(nèi)容單獨(dú)抽離成一個(gè)組件
  • 給 Text 組件增加一些裝飾
  • 用MaterialApp 和 Scaffold兩個(gè)組件裝飾 App
  • Text 組件
  • Container 組件
  • 圖片組件
  • 引入本地圖片
  • 裁剪布局之 ClipRect、ClipRRect、ClipOval、ClipPath、CustomClipper
  • 實(shí)現(xiàn)圓角以及實(shí)現(xiàn)圓形圖片
    • 圓形頭像
      • ClipOval
      • ② CircleAvatar
      • ③ BoxDecoration BoxShape.circle
    • 圓角頭像
      • ① ClipRRect
      • ② BoxDecoration BoxShape.rectangle
  • 列表組件概述
  • 列表參數(shù)
  • 基本列表
  • 水平列表
  • 動(dòng)態(tài)列表(動(dòng)態(tài)循環(huán)數(shù)據(jù))
  • GridView 組件的常用參數(shù)
  • GridView.count 實(shí)現(xiàn)網(wǎng)格布局
  • GridView.builder 實(shí)現(xiàn)網(wǎng)格布局
  • Paddiing 組件
  • Row 水平布局組件
  • Column 垂直布局組件
  • Expanded 類似 Web 中的 Flex 布局
  • Stack 組件
  • Stack Align
  • Stack Positioned
  • AspectRatio 組件
  • Card 組件
  • Card 組件實(shí)現(xiàn)一個(gè)圖文列表布局
  • RaisedButton 定義一個(gè)按鈕
  • Wrap 組件
  • 自定義有狀態(tài)組件
  • BottomNavigationBar 組件
  • 路由
  • 基本路由使用
  • 基本路由跳轉(zhuǎn)傳值
  • 命名路由
  • 命名路由跳轉(zhuǎn)傳值
  • 命名路由單獨(dú)抽離到一個(gè)文件
  • 返回到上一級頁面
  • 替換路由
  • 返回到根路由
  • AppBar 自定義頂部按鈕圖標(biāo)、顏色
  • AppBar 中自定義 TabBar 實(shí)現(xiàn)頂部 Tab 切換
  • 把 TabBar 放在導(dǎo)航最頂部
  • AppBar 中自定義 TabBar 實(shí)現(xiàn) Tabs 的另一種方法。
  • Flutter Drawer 側(cè)邊欄
  • DrawerHeader
  • UserAccountsDrawerHeader
  • 側(cè)邊欄路由跳轉(zhuǎn)
  • 按鈕組件介紹
  • 按鈕組件中的一些屬性
  • FloatingActionButton 介紹
  • 常用表單介紹
  • TextField 文本框組件
  • Checkbox、CheckboxListTile 多選框組件
  • Radio、RadioListTile 單選按鈕組件
  • 開關(guān) Switch
  • 日期和時(shí)間戳
  • 第三方庫 date_format 的使用
  • 調(diào)用自帶日期組件和時(shí)間組件
  • 調(diào)用自帶日期組件和時(shí)間組件改為中文
  • 調(diào)用第三方時(shí)間組件
  • 輪播圖組件
  • 一、AlertDialog
  • 二、SimpleDialog
  • 三、showModalBottomSheet
  • 四、showToast
  • 自定義Dialog
  • 定時(shí)器
  • 定時(shí)器結(jié)合 Dialog
  • JSON 字符串和 Map 類型的轉(zhuǎn)換(小項(xiàng)目)
  • 使用 http 庫進(jìn)行網(wǎng)絡(luò)請求
  • Dio 庫
  • 下拉刷新和上拉分頁
  • 下拉刷新
  • 上拉分頁加載更多
    • 核心代碼
    • 完整代碼
    • 解決請求重復(fù)問題
    • 滾動(dòng)條回到頂部
    • 參考代碼
  • 實(shí)現(xiàn)一個(gè)簡單的新聞 APP
  • 解析 html
  • WebView 組件 inappbrowser的使用
  • 獲取設(shè)備信息
  • 使用高德定位準(zhǔn)備工作獲取 key
  • 實(shí)現(xiàn)用高德定位
  • image_picker 實(shí)現(xiàn)相機(jī)拍照和相冊選擇
  • 上傳圖片到服務(wù)器
  • 視頻播放
  • chewie 視頻播放完整 demo
  • 檢測網(wǎng)絡(luò)
  • 檢測網(wǎng)絡(luò)完整 demo
  • 本地存儲(chǔ)
  • 本地存儲(chǔ)里面常用的一些方法
  • 本地存儲(chǔ)完整 demo
  • 掃描二維碼條形碼插件
  • 檢測應(yīng)用版本號、服務(wù)器下載文件以及實(shí)現(xiàn) App 自動(dòng)升級、安裝
    • 1、Android App 升級執(zhí)行流程
    • 2、升級 app 之前的準(zhǔn)備工作配置權(quán)限
    • 3、Android 升級 app 涉及的 API 庫
    • 4、獲取版本信息
    • 5、獲取文件存儲(chǔ)路徑
    • 6、下載文件
    • 7、打開文件
    • 8、注意事項(xiàng)
    • 完整代碼
  • 調(diào)用 url_launcher 模塊打開外部瀏覽器 打開外部應(yīng)用 撥打電話 發(fā)送短信
  • Android 修改應(yīng)用名稱、應(yīng)用圖標(biāo)、應(yīng)用啟動(dòng)畫面
    • **1**、Android 修改應(yīng)用名稱
    • **2**、Android 修改應(yīng)用圖標(biāo)
    • **3**、Android 修改應(yīng)用啟動(dòng)畫面
  • 豎向 ListView 嵌套橫向 ListView ,以及ListView 嵌套 GridView
  • 不同終端屏幕適配問題
  • JSON 序列化反序列化(模型類)
  • JSON字符串和Map類型的轉(zhuǎn)換 dart:convert手動(dòng)序列化 JSON
  • 在模型類中序列化 JSON
  • json_to_dart 自動(dòng)生成模型類
  • IndexedStack 保持頁面狀態(tài)
  • AutomaticKeepAliveClientMixin 保持頁面狀態(tài)
  • 通過事件打開側(cè)邊欄
  • 修改主題樣式
  • 下拉菜單 showMenu
  • 彈出底部菜單
  • StatefulBuilder更新Flutter showDialog 、showModalBottomSheet 中的狀態(tài)
  • 狀態(tài)管理
  • provider庫和flutter provide庫
  • provider 的使用
  • event_bus 事件廣播 事件監(jiān)聽
  • MediaQuery.removePadding移除元素的pandding
  • 瀑布流布局
  • Sliver牛逼!!!
  • 適配夜間模式
    • 夜間模式跟隨系統(tǒng)
    • 手動(dòng)開啟夜間模式
    • 保存用戶配置
    • 狀態(tài)管理
    • 通用夜間模式Provider Model類
    • MaterialApp修改
  • 登錄注冊案例
  • Flutter SliverAppBar 隱藏/顯示導(dǎo)航欄
  • 骨架屏
  • flutter 全屏背景圖(包括appbar和狀態(tài)欄)
  • 極光推送:
  • 指紋

List里面常用的屬性和方法:

/* List里面常用的屬性和方法:常用屬性:length 長度reversed 翻轉(zhuǎn)isEmpty 是否為空isNotEmpty 是否不為空常用方法: add 增加addAll 拼接數(shù)組indexOf 查找 傳入具體值remove 刪除 傳入具體值removeAt 刪除 傳入索引值fillRange 修改 insert(index,value); 指定位置插入 insertAll(index,list) 指定位置插入ListtoList() 其他類型轉(zhuǎn)換成List join() List轉(zhuǎn)換成字符串split() 字符串轉(zhuǎn)化成ListforEach mapwhereanyevery*/void main(){// List myList=['香蕉','蘋果','西瓜'];// print(myList[1]);// var list=new List();// list.add('111');// list.add('222');// print(list);//List里面的屬性:// List myList=['香蕉','蘋果','西瓜'];// print(myList.length);// print(myList.isEmpty);// print(myList.isNotEmpty);// print(myList.reversed); //對列表倒序排序// var newMyList=myList.reversed.toList();// print(newMyList);//List里面的方法:// List myList=['香蕉','蘋果','西瓜'];//myList.add('桃子'); //增加數(shù)據(jù) 增加一個(gè)// myList.addAll(['桃子','葡萄']); //拼接數(shù)組// print(myList);//print(myList.indexOf('蘋x果')); //indexOf查找數(shù)據(jù) 查找不到返回-1 查找到返回索引值// myList.remove('西瓜');// myList.removeAt(1);// print(myList);// List myList=['香蕉','蘋果','西瓜'];// myList.fillRange(1, 2,'aaa'); //修改// myList.fillRange(1, 3,'aaa'); // myList.insert(1,'aaa'); //插入 一個(gè)// myList.insertAll(1, ['aaa','bbb']); //插入 多個(gè)// print(myList);// List myList=['香蕉','蘋果','西瓜'];// var str=myList.join('-'); //list轉(zhuǎn)換成字符串// print(str);// print(str is String); //truevar str='香蕉-蘋果-西瓜';var list=str.split('-');print(list);print(list is List);}

Set

//Set //用它最主要的功能就是去除數(shù)組重復(fù)內(nèi)容//Set是沒有順序且不能重復(fù)的集合,所以不能通過索引去獲取值void main(){// var s=new Set();// s.add('香蕉');// s.add('蘋果');// s.add('蘋果');// print(s); //{香蕉, 蘋果}// print(s.toList()); List myList=['香蕉','蘋果','西瓜','香蕉','蘋果','香蕉','蘋果'];var s=new Set();s.addAll(myList);print(s);print(s.toList());}

Map

/*映射(Maps)是無序的鍵值對:常用屬性:keys 獲取所有的key值values 獲取所有的value值isEmpty 是否為空isNotEmpty 是否不為空常用方法:remove(key) 刪除指定key的數(shù)據(jù)addAll({...}) 合并映射 給映射內(nèi)增加屬性containsValue 查看映射內(nèi)的值 返回true/falseforEach mapwhereanyevery*/void main(){// Map person={// "name":"張三",// "age":20// };// var m=new Map();// m["name"]="李四";// print(person);// print(m);//常用屬性:// Map person={// "name":"張三",// "age":20,// "sex":"男"// };// print(person.keys.toList());// print(person.values.toList());// print(person.isEmpty);// print(person.isNotEmpty);//常用方法:Map person={"name":"張三","age":20,"sex":"男"};// person.addAll({// "work":['敲代碼','送外賣'],// "height":160// });// print(person);// person.remove("sex");// print(person);print(person.containsValue('張三')); }

forEach,map, where,any,every

/*forEach map where anyevery */ void main(){// List myList=['香蕉','蘋果','西瓜'];// for(var i=0;i<myList.length;i++){// print(myList[i]);// }// for(var item in myList){// print(item);// }// myList.forEach((value){// print("$value");// });// List myList=[1,3,4];// List newList=new List();// for(var i=0;i<myList.length;i++){// newList.add(myList[i]*2);// }// print(newList);// List myList=[1,3,4]; // var newList=myList.map((value){// return value*2;// });// print(newList.toList());// List myList=[1,3,4,5,7,8,9];// var newList=myList.where((value){// return value>5;// });// print(newList.toList());// List myList=[1,3,4,5,7,8,9];// var f=myList.any((value){ //只要集合里面有滿足條件的就返回true// return value>5;// });// print(f);// List myList=[1,3,4,5,7,8,9];// var f=myList.every((value){ //每一個(gè)都滿足條件返回true 否則返回false// return value>5;// });// print(f);// set// var s=new Set();// s.addAll([1,222,333]);// s.forEach((value)=>print(value));//mapMap person={"name":"張三","age":20};person.forEach((key,value){ print("$key---$value");});}

extends抽象類 和 implements

/* Dart中抽象類: Dart抽象類主要用于定義標(biāo)準(zhǔn),子類可以繼承抽象類,也可以實(shí)現(xiàn)抽象類接口。1、抽象類通過abstract 關(guān)鍵字來定義2、Dart中的抽象方法不能用abstract聲明,Dart中沒有方法體的方法我們稱為抽象方法。3、如果子類繼承抽象類必須得實(shí)現(xiàn)里面的抽象方法4、如果把抽象類當(dāng)做接口實(shí)現(xiàn)的話必須得實(shí)現(xiàn)抽象類里面定義的所有屬性和方法。5、抽象類不能被實(shí)例化,只有繼承它的子類可以extends抽象類 和 implements的區(qū)別:1、如果要復(fù)用抽象類里面的方法,并且要用抽象方法約束自類的話我們就用extends繼承抽象類2、如果只是把抽象類當(dāng)做標(biāo)準(zhǔn)的話我們就用implements實(shí)現(xiàn)抽象類*/

Flutter環(huán)境搭建

安裝最新的 Xcode

下載androidstudio

https://developer.android.google.cn/studio

下載 Flutter SDK

https://flutter.dev/docs/development/tools/sdk/releases?tab=macos

把下載好的 Flutter SDK 隨便減壓到你想安裝 Sdk 的目錄如

/Users/cc/flutter

Flutter 安裝目錄的 bin 目錄配置到環(huán)境變量,然后把 Flutter 國內(nèi)鏡像也配置到環(huán)境 變量里面

vim ~/.zshrc export PATH=/Users/cc/flutter/bin:$PATH export ANDROID_HOME="/Users/cc/Library/Android/sdk" export PATH=${PATH}:${ANDROID_HOME}/tools export PATH=${PATH}:${ANDROID_HOME}/platform-tools export PUB_HOSTED_URL=https://pub.flutter-io.cn export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn source ~/.zshrc

flutter -h 如果能出來一些命令說明 flutter sdk 配置成功。

注意如果配置完成后輸入 flutter -h 告訴你 flutter 不是內(nèi)置命令之類的錯(cuò)誤的話,可能 sdk 沒有配置成功,也可能 sdk 下載的時(shí)候沒有下載全

運(yùn)行 flutter doctor 命令檢測環(huán)境

入口文件、入口方法

每一個(gè) flutter 項(xiàng)目的 lib 目錄里面都有一個(gè) main.dart 這個(gè)文件就是 flutter 的入口文件

main.dart 里面的

void main() {runApp(MyApp()); } //也可以簡寫 void main() => runApp(MyApp());

其中的 main 方法是 dart 的入口方法。runApp 方法是 flutter 的入口方法。 MyApp 是自定義的一個(gè)組件

第一個(gè) Demo Center 組件的 使用

import 'package:flutter/material.dart';void main() {runApp(Center(child: Text("我是一個(gè)文本內(nèi)容",textDirection: TextDirection.ltr,),)); }

把內(nèi)容單獨(dú)抽離成一個(gè)組件

在 Flutter 中自定義組件其實(shí)就是一個(gè)類,這個(gè)類需要繼承 StatelessWidget/StatefulWidget

前期我們都繼承 StatelessWidget。后期給大家講 StatefulWidget 的使用。

StatelessWidget 是無狀態(tài)組件,狀態(tài)不可變的 widget
StatefulWidget 是有狀態(tài)組件,持有的狀態(tài)可能在 widget 生命周期改變

import 'package:flutter/material.dart';void main() {runApp(MyApp()); }class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return Center(child: Text("我是一個(gè)文本內(nèi)容",textDirection: TextDirection.ltr,),);} }

給 Text 組件增加一些裝飾

import 'package:flutter/material.dart';void main() {runApp(MyApp()); }class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return Center(child: Text("我是一個(gè)文本內(nèi)容",textDirection: TextDirection.ltr,style: TextStyle(fontSize: 40.0,fontWeight: FontWeight.bold,// color: Colors.yellowcolor: Color.fromRGBO(255, 222, 222, 0.5)),),);} }

用MaterialApp 和 Scaffold兩個(gè)組件裝飾 App

1MaterialApp

MaterialApp 是一個(gè)方便的 Widget,它封裝了應(yīng)用程序?qū)崿F(xiàn) Material Design 所需要的 一些 Widget。一般作為頂層 widget 使用。

常用的屬性:

home(主頁)

title(標(biāo)題)

color(顏色)

theme(主題)

routes(路由)

2Scaffold

Scaffold 是 Material Design 布局結(jié)構(gòu)的基本實(shí)現(xiàn)。此類提供了用于顯示 drawer、snackbar 和底部 sheet 的 API。

Scaffold 有下面幾個(gè)主要屬性:

appBar - 顯示在界面頂部的一個(gè) AppBar。

body - 當(dāng)前界面所顯示的主要內(nèi)容 Widget。

drawer - 抽屜菜單控件。

import 'package:flutter/material.dart';void main() {runApp(MyApp()); }class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(title: "我是一個(gè)標(biāo)題",home: Scaffold(appBar: AppBar(title: Text("Hello Flutter"),elevation: 30.0, 設(shè)置標(biāo)題陰影 不需要的話值設(shè)置成 0.0),body: HomeContent(),),theme: ThemeData(//設(shè)置主題顏色primarySwatch: Colors.yellow),);} }class HomeContent extends StatelessWidget {@overrideWidget build(BuildContext context) {return Center(child: Text("我是一個(gè)文本內(nèi)容",textDirection: TextDirection.ltr,style: TextStyle(fontSize: 40.0,fontWeight: FontWeight.bold,// color: Colors.yellowcolor: Color.fromRGBO(255, 222, 222, 0.5)),),);} }

Text 組件

名稱功能
textAlign文本對齊方式(center 居中,left 左 對齊,right 右對齊,justfy 兩端對齊)
textDirection文本方向(ltr 從左至右,rtl 從右至 左)
overflow文字超出屏幕之后的處理方式(clip 裁剪,fade 漸隱,ellipsis 省略號)
textScaleFactor字體顯示倍率
maxLines文字顯示最大行數(shù)
style字體的樣式設(shè)置

下面是 TextStyle 的參數(shù) :

名稱功能
decoration文字裝飾線(none 沒有線,lineThrough 刪 除線,overline 上劃線,underline 下劃線)
decorationColor文字裝飾線顏色
decorationStyle文字裝飾線風(fēng)格([dashed,dotted]虛線, double 兩根線,solid 一根實(shí)線,wavy 波浪 線)
wordSpacing單詞間隙(如果是負(fù)值,會(huì)讓單詞變得更緊 湊
letterSpacing字母間隙(如果是負(fù)值,會(huì)讓字母變得更緊 湊)
fontStyle文字樣式(italic 斜體,normal 正常體)
fontSize文字大小
color文字顏色
fontWeight字體粗細(xì)(bold 粗體,normal 正常體)

更多參數(shù):https://docs.flutter.io/flutter/painting/TextStyle-class.html

Container 組件

名稱功能
alignmenttopCenter:頂部居中對齊
topLeft:頂部左對齊
topRight:頂部右對齊
center:水平垂直居中對齊
centerLeft:垂直居中水平居左對齊
centerRight:垂直居中水平居右對齊
bottomCenter 底部居中對齊
bottomLeft:底部居左對齊
bottomRight:底部居右對齊
decorationdecoration: BoxDecoration(
color: Colors.blue,
border: Border.all(
color: Colors.red,
width: 2.0,
),
borderRadius:
BorderRadius.all(
Radius.circular(8.0)
)
)
marginmargin 屬性是表示 Container 與外部其他 組件的距離。
EdgeInsets.all(20.0),
paddingpadding 就是 Container 的內(nèi)邊距,指 Container 邊緣與 Child 之間的距離
padding: EdgeInsets.all(10.0)
transform讓 Container 容易進(jìn)行一些旋轉(zhuǎn)之類的
transform: Matrix4.rotationZ(0.2)
height容器高度
width容器寬度
child容器子元素

更多參數(shù):https://api.flutter.dev/flutter/widgets/Container-class.html

圖片組件

圖片組件是顯示圖像的組件,Image 組件有很多構(gòu)造函數(shù),這里我們只給大家講兩個(gè)

Image.asset 本地圖片

Image.network 遠(yuǎn)程圖片

Image 組件的常用屬性:

名稱類型說明
alignmentalignment圖片的對齊方式
color 和 colorBlendMode設(shè)置圖片的背景顏色,通常和 colorBlendMode 配合一起使用,這樣可以是圖片顏色和背景色混合。上面的圖片就是進(jìn)行了顏色的混合,綠色背景和圖片紅色的混合
fitBoxFitfit 屬性用來控制圖片的拉伸和擠壓,這都是根據(jù)父容器來的。
BoxFit.fill:全圖顯示,圖片會(huì)被拉伸,并充滿父容器。
BoxFit.contain:全圖顯示,顯示原比例,可能會(huì)有空隙。
BoxFit.cover:顯示可能拉伸,可能裁切,充滿(圖片要充滿整個(gè)容器,還不變形)。
BoxFit.fitWidth:寬度充滿(橫向充滿),顯示可能拉伸,可能裁切。
BoxFit.fitHeight :高度充滿(豎向充滿),顯示可能拉伸,可能裁切。
BoxFit.scaleDown:效果和 contain 差不多,但是此屬性不允許顯示超過源圖片大小,可小不可大。
repeat平鋪ImageRepeat.repeat : 橫向和縱向都進(jìn)行重復(fù),直到鋪滿整個(gè)畫布。
ImageRepeat.repeatX: 橫向重復(fù),縱向不重復(fù)。
ImageRepeat.repeatY:縱向重復(fù),橫向不重復(fù)。
width寬度 一般結(jié)合 ClipOval 才能看到效果
height高度 一般結(jié)合 ClipOval 才能看到效果

更多屬性參考:https://api.flutter.dev/flutter/widgets/Image-class.html

return Center(child: Container(child: Image.network("http://pic.baike.soso.com/p/20130828/20130828161137-1346445960.jpg",alignment: Alignment.topLeft,color: Colors.red,colorBlendMode: BlendMode.colorDodge,// repeat: ImageRepeat.repeatX,fit: BoxFit.cover,),width: 300.0,height: 400.0,decoration: BoxDecoration(color: Colors.yellow),),);

引入本地圖片

emmm…不記了

裁剪布局之 ClipRect、ClipRRect、ClipOval、ClipPath、CustomClipper

widget 作用
ClipRect 將 child 剪裁為給定的矩形大小
ClipRRect 將 child 剪裁為圓角矩形
ClipOval 如果 child 為正方形時(shí)剪裁之后是圓形,如果 child 為矩形時(shí),剪裁之后為橢圓形
ClipPath 將 child 按照給定的路徑進(jìn)行裁剪
CustomClipper 并不是一個(gè)widget,但是使用CustomClipper可以繪制出任何我們想要的形狀

實(shí)現(xiàn)圓角以及實(shí)現(xiàn)圓形圖片

實(shí)現(xiàn)圓角圖片

return Center(child: Container(width: 300.0,height: 300.0,decoration: BoxDecoration(color: Colors.yellow,borderRadius: BorderRadius.circular(150),image: DecorationImage(image: NetworkImage("http://pic.baike.soso.com/p/20130828/20130828161137-1346445960.jpg",),fit: BoxFit.cover)),),);

實(shí)現(xiàn)圓形圖片

return Center(child: Container(child: ClipOval(child: Image.network("https://www.itying.com/images/201905/thumb_img/1101_thumb_G_1557845381862.jpg",width: 150.0,height: 150.0,),),),);

圓形頭像

ClipOval

new ClipOval(child: new Image.asset(Utils.getImgPath('ali_connors')),)

② CircleAvatar

new CircleAvatar(radius: 36.0,backgroundImage: AssetImage(Utils.getImgPath('ali_connors'),),)

③ BoxDecoration BoxShape.circle

new Container(width: 72.0,height: 72.0,decoration: BoxDecoration(shape: BoxShape.circle,image: DecorationImage(image: AssetImage(Utils.getImgPath('ali_connors'),),),),)

圓角頭像

① ClipRRect

new ClipRRect(borderRadius: BorderRadius.circular(6.0),child: new Image.asset(Utils.getImgPath('ali_connors')),)

② BoxDecoration BoxShape.rectangle

new Container(width: 88.0,height: 88.0,decoration: BoxDecoration(shape: BoxShape.rectangle,borderRadius: BorderRadius.circular(6.0),image: DecorationImage(image: AssetImage(Utils.getImgPath('ali_connors'),),),),

列表組件概述

列表布局是我們項(xiàng)目開發(fā)中最常用的一種布局方式。Flutter 中我們可以通過 ListView 來定義 列表項(xiàng),支持垂直和水平方向展示。通過一個(gè)屬性就可以控制列表的顯示方向。列表有一下 分類:

**1、垂直列表(寬度自動(dòng)擴(kuò)展,設(shè)置寬度無效)**可以在外層包Container控制

2、垂直圖文列表

**3、水平列表(高度自動(dòng)擴(kuò)展,設(shè)置高度無效)**可以在外層包Container控制

4、動(dòng)態(tài)列表

5、矩陣式列表(網(wǎng)格布局)

列表參數(shù)

名稱類型說明
scrollDirectionAxisAxis.horizontal 水平列表
Axis.vertical 垂直列表
paddingEdgeInsetsGeometry內(nèi)邊距
resolvebool組件反向排序
childrenList列表元素

基本列表

class HomeContent extends StatelessWidget {@overrideWidget build(BuildContext context) {return Center(child: ListView(children: <Widget>[ListTile(leading: Icon(Icons.phone),title: Text("this is list", style: TextStyle(fontSize: 28.0)),subtitle: Text('this is list this is list'),),ListTile(title: Text("this is list"),subtitle: Text('this is list this is list'),trailing: Icon(Icons.phone),),ListTile(title: Text("this is list"),subtitle: Text('this is list this is list'),),ListTile(title: Text("this is list"),subtitle: Text('this is list this is list'),),ListTile(title: Text("this is list"),subtitle: Text('this is list this is list'),)],),);} }

水平列表

class HomeContent extends StatelessWidget {@overrideWidget build(BuildContext context) {return Container(height: 200.0,margin: EdgeInsets.all(5),child: ListView(scrollDirection: Axis.horizontal,children: <Widget>[Container(width: 180.0,color: Colors.lightBlue,),Container(width: 180,color: Colors.amber,child: ListView(children: <Widget>[Image.network("https://resources.ninghao.org/images/childhood-in-a-picture.jpg"),SizedBox(height: 16.0),Text("這是一個(gè)文本信息",textAlign: TextAlign.center,style: TextStyle(fontSize: 16.0)),],),)],),);} }

動(dòng)態(tài)列表(動(dòng)態(tài)循環(huán)數(shù)據(jù))

import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text("Hello Flutter")),body: HomeContent(),),);} }class HomeContent extends StatelessWidget {List list = List();HomeContent() {for (int i = 0; i < 20; i++) {list.add("這是第$i條數(shù)據(jù)");}print(list);}@overrideWidget build(BuildContext context) {return ListView.builder(itemCount: this.list.length,itemBuilder: (context, index) {// print(context);return ListTile(leading: Icon(Icons.phone),title: Text("${list[index]}"),);});} }

GridView 組件的常用參數(shù)

當(dāng)數(shù)據(jù)量很大的時(shí)候用矩陣方式排列比較清晰。此時(shí)我們可以用網(wǎng)格列表組件 GridView 實(shí)現(xiàn)布局。

GridView 創(chuàng)建網(wǎng)格列表有多種方式,下面我們主要介紹兩種。

1、可以通過 GridView.count 實(shí)現(xiàn)網(wǎng)格布局

2、通過 GridView.builder 實(shí)現(xiàn)網(wǎng)格布局

常用屬性:

名稱類型說明
scrollDirectionAxis滾動(dòng)方法
paddingEdgeInsetsGeometry內(nèi)邊距
resolvebool組件反向排序
crossAxisSpacingdouble水平子 Widget 之間間距
mainAxisSpacingdouble垂直子 Widget 之間間距
crossAxisCountint一行的 Widget 數(shù)量
childAspectRatiodouble子 Widget 寬高比例
children[ ]
gridDelegateSliverGridDelegateWithFix
edCrossAxisCount(常用)
SliverGridDelegateWithMax
CrossAxisExtent
控制布局主要用在GridView.builder 里面

GridView.count 實(shí)現(xiàn)網(wǎng)格布局

import 'package:cc/res/listData.dart'; import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text("Hello Flutter")),body: LayoutContent(),),);} }class LayoutContent extends StatelessWidget {List<Widget> _getListData() {var tempList = listData.map((value) {return Container(child: Column(children: <Widget>[Image.network(value["imageUrl"]),SizedBox(height: 12),Text(value["title"],textAlign: TextAlign.center, style: TextStyle(fontSize: 20))],),decoration: BoxDecoration(border: Border.all(color: Color.fromRGBO(230, 230, 230, 0.9), width: 1.0)),);});// ('124124','124214')return tempList.toList();}@overrideWidget build(BuildContext context) {return GridView.count(crossAxisCount: 2,crossAxisSpacing: 20,mainAxisSpacing: 20, // childAspectRatio:0.7,children: this._getListData(),);} }

GridView.builder 實(shí)現(xiàn)網(wǎng)格布局

import 'package:cc/res/listData.dart'; import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text("Hello Flutter")),body: LayoutContent(),),);} }class LayoutContent extends StatelessWidget {Widget _getListData(context, index) {return Container(child: Column(children: <Widget>[Image.network(listData[index]["imageUrl"]),SizedBox(height: 12),Text(listData[index]["title"],textAlign: TextAlign.center, style: TextStyle(fontSize: 20))],),decoration: BoxDecoration(border: Border.all(color: Color.fromRGBO(230, 230, 230, 0.9), width: 1.0)),);}@overrideWidget build(BuildContext context) {return GridView.builder(itemCount: listData.length,gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(//橫軸元素個(gè)數(shù)crossAxisCount: 2,//縱軸間距mainAxisSpacing: 20.0,//橫軸間距crossAxisSpacing: 10.0,//子組件寬高長度比例childAspectRatio: 1.0),itemBuilder: this._getListData,);} }

Paddiing 組件

在 html 中常見的布局標(biāo)簽都有 padding 屬性,但是 Flutter 中很多 Widget 是沒有 padding 屬性。這個(gè)時(shí)候我們可以用 Padding 組件處理容器與子元素直接的間距。

屬性說明
paddingpadding 值, EdgeInsetss 設(shè)置填充的值
child子組件
class LayoutDemo extends StatelessWidget {@overrideWidget build(BuildContext context) {return Padding(padding: EdgeInsets.fromLTRB(0, 0, 10, 0),child: GridView.count(crossAxisCount: 2,childAspectRatio: 1.5,children: <Widget>[Padding(padding: EdgeInsets.fromLTRB(10, 10, 0, 0),child: Image.network("https://www.itying.com/images/flutter/1.png",fit: BoxFit.cover),),Padding(padding: EdgeInsets.fromLTRB(10, 10, 0, 0),child: Image.network("https://www.itying.com/images/flutter/2.png",fit: BoxFit.cover),),Padding(padding: EdgeInsets.fromLTRB(10, 10, 0, 0),child: Image.network("https://www.itying.com/images/flutter/3.png",fit: BoxFit.cover),),Padding(padding: EdgeInsets.fromLTRB(10, 10, 0, 0),child: Image.network("https://www.itying.com/images/flutter/4.png",fit: BoxFit.cover),),Padding(padding: EdgeInsets.fromLTRB(10, 10, 0, 0),child: Image.network("https://www.itying.com/images/flutter/5.png",fit: BoxFit.cover),),Padding(padding: EdgeInsets.fromLTRB(10, 10, 0, 0),child: Image.network("https://www.itying.com/images/flutter/6.png",fit: BoxFit.cover),),],),);} }

Row 水平布局組件

屬性說明
mainAxisAlignment主軸的排序方式
crossAxisAlignment次軸的排序方式
children組件子元素
import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text("Hello Flutter")),body: LayoutDemo(),),);} }class LayoutDemo extends StatelessWidget {@overrideWidget build(BuildContext context) {return Container(height: 700,width: 500,color: Colors.black26,child: Row(crossAxisAlignment: CrossAxisAlignment.center,mainAxisAlignment: MainAxisAlignment.spaceEvenly, // crossAxisAlignment: CrossAxisAlignment.center,children: <Widget>[IconContainer(Icons.home, color: Colors.red),IconContainer(Icons.home, color: Colors.blue),IconContainer(Icons.home, color: Colors.orange),],),);} }class IconContainer extends StatelessWidget {double size;IconData icon;Color color;IconContainer(this.icon, {this.size, this.color = Colors.blue}) {this.size = 32.0;}@overrideWidget build(BuildContext context) {return Container(width: this.size + 60,height: this.size + 60,color: this.color,child: Center(child: Icon(this.icon,color: Colors.white,size: this.size,)),);} }

Column 垂直布局組件

屬性說明
mainAxisAlignment主軸的排序方式
crossAxisAlignment次軸的排序方式
children組件子元素
import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text("Hello Flutter")),body: LayoutDemo(),),);} }class LayoutDemo extends StatelessWidget {@overrideWidget build(BuildContext context) {return Container(height: 700,width: 500,color: Colors.black26,child: Column(crossAxisAlignment: CrossAxisAlignment.center,mainAxisAlignment: MainAxisAlignment.spaceEvenly, // crossAxisAlignment: CrossAxisAlignment.center,children: <Widget>[IconContainer(Icons.home, color: Colors.red),IconContainer(Icons.home, color: Colors.blue),IconContainer(Icons.home, color: Colors.orange),],),);} }class IconContainer extends StatelessWidget {double size;IconData icon;Color color;IconContainer(this.icon, {this.size, this.color = Colors.blue}) {this.size = 32.0;}@overrideWidget build(BuildContext context) {return Container(width: this.size + 60,height: this.size + 60,color: this.color,child: Center(child: Icon(this.icon,color: Colors.white,size: this.size,)),);} }

Expanded 類似 Web 中的 Flex 布局

Expanded 可以用在 Row 和 Column 布局中

屬性說明
flex元素站整個(gè)父 Row /Column 的比例
child子元素
import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text("Hello Flutter")),body: LayoutDemo(),),);} }class LayoutDemo extends StatelessWidget {@overrideWidget build(BuildContext context) {return Padding(padding: EdgeInsets.all(10),child: Row(// crossAxisAlignment: CrossAxisAlignment.start,mainAxisAlignment: MainAxisAlignment.center,// crossAxisAlignment: CrossAxisAlignment.center,children: <Widget>[Expanded(flex: 2, child: IconContainer(Icons.home)),SizedBox(width: 10),Expanded(flex: 3, child: IconContainer(Icons.search)),// SizedBox(width: 10),// Expanded(child: IconContainer(Icons.send))],),);} }class IconContainer extends StatelessWidget {double size;IconData icon;IconContainer(this.icon, {this.size}) {this.size = 32.0;}@overrideWidget build(BuildContext context) {return Container(width: 100.0,height: 100.0,color: Colors.blue,child:Center(child: Icon(this.icon, color: Colors.white, size: this.size)),);} }

Stack 組件

Stack 表示堆的意思,我們可以用 Stack 或者 Stack 結(jié)合 Align 或者 Stack 結(jié)合 Positiond 來實(shí)現(xiàn)頁面的定位布局

屬性說明
alignment配置所有子元素的顯示位置
children子組件
class LayoutDemo extends StatelessWidget {@overrideWidget build(BuildContext context) {// TODO: implement buildreturn Center(child: Stack(alignment: Alignment.topLeft,children: <Widget>[ Container(height: 400,width: 300,color: Colors.red,),Text('我是一個(gè)文本',style: TextStyle(fontSize: 40,color: Colors.white)) ],),);} } class LayoutDemo extends StatelessWidget {@overrideWidget build(BuildContext context) {// TODO: implement buildreturn Center(child: Stack(alignment: Alignment(1,0.3),children: <Widget>[ Container(height: 400,width: 300,color: Colors.red,),Text('我是一個(gè)文本',style: TextStyle(fontSize: 20,color: Colors.white)) ],),);} }

Stack Align

Stack 組件中結(jié)合 Align 組件可以控制每個(gè)子元素的顯示位置

屬性說明
alignment配置所有子元素的顯示位置
child子組件
class LayoutDemo extends StatelessWidget {@overrideWidget build(BuildContext context) {// TODO: implement buildreturn Center(child: Container(height: 400,width: 300,color: Colors.red,child: Stack(// alignment: Alignment.center,children: <Widget>[Align(alignment: Alignment(1,-0.2),child: Icon(Icons.home,size: 40,color: Colors.white),),Align(alignment: Alignment.center,child: Icon(Icons.search,size: 30,color: Colors.white),),Align(alignment: Alignment.bottomRight,child: Icon(Icons.settings_applications,size: 30,color: Colors.white),)],),),);} }

Stack Positioned

Stack 組件中結(jié)合 Positioned 組件也可以控制每個(gè)子元素的顯示位置

屬性說明
top子元素距離頂部的距離
bottom子元素距離底部的距離
left子元素距離左側(cè)距離
right子元素距離右側(cè)距離
child子組件
class LayoutDemo extends StatelessWidget {@overrideWidget build(BuildContext context) {// TODO: implement buildreturn Center(child: Container(height: 400,width: 300,color: Colors.red,child: Stack(// alignment: Alignment.center,children: <Widget>[Positioned(// left: 10,child: Icon(Icons.home,size: 40,color: Colors.white),),Positioned(bottom: 0,left: 100,child: Icon(Icons.search,size: 30,color: Colors.white),),Positioned(right: 0,child: Icon(Icons.settings_applications,size: 30,color: Colors.white),)],),),);} }

AspectRatio 組件

AspectRatio 的作用是根據(jù)設(shè)置調(diào)整子元素 child 的寬高比。

AspectRatio 首先會(huì)在布局限制條件允許的范圍內(nèi)盡可能的擴(kuò)展,widget 的高度是由寬度和比率決定的,類似于 BoxFit 中的 contain,按照固定比率去盡量占滿區(qū)域。

如果在滿足所有限制條件過后無法找到一個(gè)可行的尺寸,AspectRatio 最終將會(huì)去優(yōu)先適應(yīng)布局限制條件,而忽略所設(shè)置的比率。

屬性說明
aspectRatio寬高比,最終可能不會(huì)根據(jù)這個(gè)值去布局,具體則要看綜合因素,外層是否允許按照這種比率進(jìn)行布局,這只是一個(gè)參考值
child子組件
return Center(child: Container(width: 200,child: AspectRatio(aspectRatio: 2.0 / 1.0,child: Container(color: Colors.red,),),),);

Card 組件

Card 是卡片組件塊,內(nèi)容可以由大多數(shù)類型的 Widget 構(gòu)成,Card 具有圓角和陰影,這讓它看起來有立體感。

屬性說明
margin外邊距
child子組件
ShapeCard 的陰影效果,默認(rèn)的陰影效果為圓角的長方形邊。
class LayoutDemo extends StatelessWidget {@overrideWidget build(BuildContext context) {return ListView(children: <Widget>[Card(margin: EdgeInsets.all(10),child: Column(children: <Widget>[ListTile(title: Text("張三",style: TextStyle(fontSize: 28),),subtitle: Text("高級軟件工程師"),),Divider(),ListTile(title: Text("電話:123123123"),),ListTile(title: Text("地址:北京市海淀區(qū)"),)],),),Card(margin: EdgeInsets.all(10),child: Column(children: <Widget>[ListTile(title: Text("李四",style: TextStyle(fontSize: 28),),subtitle: Text("高級軟件工程師"),),Divider(),ListTile(title: Text("電話:123123123"),),ListTile(title: Text("地址:北京市海淀區(qū)"),)],),),],);} }

Card 組件實(shí)現(xiàn)一個(gè)圖文列表布局

class LayoutDemo extends StatelessWidget {@overrideWidget build(BuildContext context) {return ListView(children: listData.map((value) {return Card(margin: EdgeInsets.all(10),child: Column(children: <Widget>[AspectRatio(aspectRatio: 16 / 9,child: Image.network(value["imageUrl"], fit: BoxFit.cover),),ListTile(leading: CircleAvatar(backgroundImage: NetworkImage(value["imageUrl"]),),title: Text(value["description"]),subtitle: Text(value["description"],overflow: TextOverflow.ellipsis,),)],),);}).toList());} } class LayoutDemo extends StatelessWidget {@overrideWidget build(BuildContext context) {return ListView.builder(itemCount: listData.length,itemBuilder: (context,index){return Card(margin: EdgeInsets.all(10),child: Column(children: <Widget>[AspectRatio(aspectRatio: 16 / 9,child: Image.network(listData[index]["imageUrl"], fit: BoxFit.cover),),ListTile(leading: CircleAvatar(backgroundImage: NetworkImage(listData[index]["imageUrl"]),),title: Text(listData[index]["description"]),subtitle: Text(listData[index]["description"],overflow: TextOverflow.ellipsis,),)],),);},);} }

RaisedButton 定義一個(gè)按鈕

Flutter 中通過 RaisedButton 定義一個(gè)按鈕。RaisedButton 里面有很多的參數(shù),這一講我們只是簡單的進(jìn)行使用。

return RaisedButton(child: Text("Flutter"),textColor: Theme.of(context).accentColor,onPressed: (){});

Wrap 組件

Wrap 可以實(shí)現(xiàn)流布局,單行的 Wrap 跟 Row 表現(xiàn)幾乎一致,單列的 Wrap 則跟 Row 表 現(xiàn)幾乎一致。但 Row 與 Column 都是單行單列的,Wrap 則突破了這個(gè)限制,mainAxis 上空 間不足時(shí),則向 crossAxis 上去擴(kuò)展顯示。

屬性說明
direction主軸的方向,默認(rèn)水平
alignment主軸的對其方式
spacing主軸方向上的間距
textDirection文本方向
verticalDirection定義了 children 擺放順序,默認(rèn)是 down,見Flex 相關(guān)屬性介紹。
runAlignmentrun 的對齊方式。run 可以理解為新的行或者列,如果是水平方向布局的話,run 可以理解為新的一行
runSpacingrun 的間距
import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text("Hello Flutter")),body: LayoutDemo(),),);} }class LayoutDemo extends StatelessWidget {@overrideWidget build(BuildContext context) {return Wrap(spacing: 10,runSpacing: 10,alignment: WrapAlignment.spaceEvenly,children: <Widget>[MyButton("第 1 集"),MyButton("第 2 集"),MyButton("第 3 集"),MyButton("第 4 集"),MyButton("第 5 集"),MyButton("第 6 集第 6 集"),MyButton("第 7 集"),MyButton("第 8 集第 6 集"),MyButton("第 9 集"),MyButton("第 10 集"),MyButton("第 11 集"),],);} }class MyButton extends StatelessWidget {final String text;const MyButton(this.text, {Key key}) : super(key: key);@overrideWidget build(BuildContext context) {return RaisedButton(child: Text(this.text),textColor: Theme.of(context).accentColor,onPressed: () {});} }

自定義有狀態(tài)組件

在 Flutter 中自定義組件其實(shí)就是一個(gè)類,這個(gè)類需要繼承 StatelessWidget/StatefulWidget。

StatelessWidget 是無狀態(tài)組件,狀態(tài)不可變的 widget
StatefulWidget 是有狀態(tài)組件,持有的狀態(tài)可能在 widget 生命周期改變。通俗的講:如果我們想改變頁面中的數(shù)據(jù)的話這個(gè)時(shí)候就需要用到 StatefulWidget

import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text("Hello Flutter")),body: HomePage(),),);} }class HomePage extends StatefulWidget {@override_HomePageState createState() => _HomePageState(); }class _HomePageState extends State<HomePage> {int count = 0;@overrideWidget build(BuildContext context) {return Container(child: Column(children: <Widget>[Chip(label: Text("${this.count}")),RaisedButton(child: Text("增加"),onPressed: () { // print(this.count);setState(() {this.count++;});})],),);} }

BottomNavigationBar 組件

BottomNavigationBar 是底部導(dǎo)航條,可以讓我們定義底部 Tab 切換,bottomNavigationBar是 Scaffold 組件的參數(shù)。

BottomNavigationBar 常見的屬性

屬性名說明
itemsList 底 部 導(dǎo) 航條按鈕集合
iconSizeicon
currentIndex默認(rèn)選中第幾個(gè)
fixedColor選中的顏色
typeBottomNavigationBarType.fixed
BottomNavigationBarType.shifting
(上面解決4個(gè)底部導(dǎo)航顯示出錯(cuò))
class Tabs extends StatefulWidget {Tabs({Key key}) : super(key: key);_TabsState createState() => _TabsState(); }class _TabsState extends State<Tabs> {int _currentIndex=0;List _pageList=[HomePage(),CategoryPage(),SettingPage(),];@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("Flutter Demo"),),body: this._pageList[this._currentIndex],bottomNavigationBar: BottomNavigationBar(currentIndex: this._currentIndex, //配置對應(yīng)的索引值選中onTap: (int index){setState(() { //改變狀態(tài)this._currentIndex=index;});},iconSize:36.0, //icon的大小fixedColor:Colors.red, //選中的顏色 type:BottomNavigationBarType.fixed, //配置底部tabs可以有多個(gè)按鈕items: [BottomNavigationBarItem(icon: Icon(Icons.home),title: Text("首頁")),BottomNavigationBarItem(icon: Icon(Icons.category),title: Text("分類")),BottomNavigationBarItem(icon: Icon(Icons.settings),title: Text("設(shè)置"))],),);} }

路由

Flutter 中的路由通俗的講就是頁面跳轉(zhuǎn)。在 Flutter 中通過 Navigator 組件管理路由導(dǎo)航。

并提供了管理堆棧的方法。如:Navigator.push 和 Navigator.pop

Flutter 中給我們提供了兩種配置路由跳轉(zhuǎn)的方式:1、基本路由 2、命名路由

基本路由使用

比如我們現(xiàn)在想從 HomePage 組件跳轉(zhuǎn)到 SearchPage 組件。

1、需要在 HomPage 中引入 SearchPage.dart

import '../SearchPage.dart';

2、在 HomePage 中通過下面方法跳轉(zhuǎn)

RaisedButton(child: Text("跳轉(zhuǎn)到搜索頁面"),onPressed: () {Navigator.of(context).push(MaterialPageRoute(builder: (context) => SerachPage()));},color: Theme.of(context).accentColor,textTheme: ButtonTextTheme.primary,)

基本路由跳轉(zhuǎn)傳值

比如我們現(xiàn)在想從 HomePage 組件跳轉(zhuǎn)到 SearchPage 組件傳值。

1、需要在 HomPage 中引入 SearchPage.dart

import '../SearchPage.dart';

2、在 HomePage 中通過下面方法跳轉(zhuǎn)

RaisedButton(child: Text("跳轉(zhuǎn)到搜索頁面"),onPressed: () {Navigator.of(context).push(MaterialPageRoute(builder: (context) => SerachPage(title:"表單") //傳值 SerachPage加構(gòu)造函數(shù)并傳參數(shù)));},color: Theme.of(context).accentColor,textTheme: ButtonTextTheme.primary,)

命名路由

1、配置路由

return MaterialApp(// home:Tabs(),initialRoute: '/', //初始化的時(shí)候加載的路由routes: {'/':(contxt)=>Tabs(),'/search':(contxt) =>SearchPage(),'/form': (context) => FormPage(),},);

2、路由跳轉(zhuǎn)

RaisedButton(child: Text("跳轉(zhuǎn)到搜索頁面"),onPressed: () {Navigator.pushNamed(context, '/search');},color: Theme.of(context).accentColor,textTheme: ButtonTextTheme.primary)d

命名路由跳轉(zhuǎn)傳值

花里胡哨

命名路由單獨(dú)抽離到一個(gè)文件

抽個(gè)雞兒

返回到上一級頁面

Navigator.of(context).pop();

替換路由

比如我們從用戶中心頁面跳轉(zhuǎn)到了 registerFirst 頁面,然后從 registerFirst 頁面通過pushReplacementNamed 跳轉(zhuǎn)到了 registerSecond 頁面。這個(gè)時(shí)候當(dāng)我們點(diǎn)擊 registerSecond的返回按鈕的時(shí)候它會(huì)直接返回到用戶中心。

Navigator.of(context).pushReplacementNamed('/registerSecond'); //命名路由替換 // 普通路由替換 Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (context)=>Second()));

返回到根路由

比如我們從用戶中心跳轉(zhuǎn)到 registerFirst 頁面,然后從 registerFirst 頁面跳轉(zhuǎn)到 registerSecond頁面,然后從 registerSecond 跳轉(zhuǎn)到了 registerThird 頁面。這個(gè)時(shí)候我們想的是 registerThird注冊成功后返回到用戶中心。 這個(gè)時(shí)候就用到了返回到根路由的方法。

Navigator.of(context).pushAndRemoveUntil(new MaterialPageRoute(builder: (context) => new Tabs(index:1)),(route) => route == null );

AppBar 自定義頂部按鈕圖標(biāo)、顏色

屬性描述
leading在標(biāo)題前面顯示的一個(gè)控件,在首頁通常顯示應(yīng)用的 logo;在其他界面通常顯示為返回按鈕
title標(biāo)題,通常顯示為當(dāng)前界面的標(biāo)題文字,可以放組件
actions在標(biāo)題后面顯示的一個(gè)控件,通常使用 IconButton 來表示,可以放按鈕組
bottom通常放 tabBar,標(biāo)題下面顯示一個(gè) Tab 導(dǎo)航欄
backgroundColor導(dǎo)航背景顏色
iconTheme圖標(biāo)樣式
textTheme文字樣式
centerTitle標(biāo)題是否居中顯示
class AppBardemoPage extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(backgroundColor: Colors.red,leading: IconButton(icon: Icon(Icons.menu),tooltip: "Search",onPressed: () {print('Menu Pressed');}),title: Text('FlutterDemo'),actions: <Widget>[IconButton(icon: Icon(Icons.search),tooltip: "Search",onPressed: () {print('Search Pressed');}),IconButton(icon: Icon(Icons.more_horiz),tooltip: "more_horiz",onPressed: () {print('more_horiz Pressed');})],),body: Text('這是Appbar'),);} }

AppBar 中自定義 TabBar 實(shí)現(xiàn)頂部 Tab 切換

TabBar 常見屬性:

屬性描述
tabs顯示的標(biāo)簽內(nèi)容,一般使用 Tab 對象,也可以是其他的 Widget
controllerTabController 對象
isScrollable是否可滾動(dòng)(是指有很多個(gè)appbar時(shí)滾動(dòng)appbar,左右滾動(dòng)appbar。不是滾動(dòng)內(nèi)容)
indicatorColor指示器顏色
indicatorWeight指示器高度
indicatorPadding底部指示器的 Padding
indicator指示器 decoration,例如邊框等
indicatorSize指示器大小計(jì)算方式,TabBarIndicatorSize.label 跟文字等寬,TabBarIndicatorSize.tab 跟每個(gè) tab 等寬
labelColor選中 label 顏色
labelStyle選中 label 的 Style
labelPadding每個(gè) label 的 padding 值
unselectedLabelColor未選中 label 顏色
unselectedLabelStyle未選中 label 的 Style
import 'package:flutter/material.dart';class AppBardemoPage extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: DefaultTabController( //DefaultTabController在MaterialApp之后,Scaffold之前l(fā)ength: 2, //如果頁面通過路由掛載,直接return DefaultTabControllerchild: Scaffold(appBar: AppBar(title: TabBar(tabs: <Widget>[Tab(text: '熱門'),Tab(text: "123"),],),),body: TabBarView(children: <Widget>[ListView(children: <Widget>[ListTile(title: Text("這是第一個(gè) tab")),ListTile(title: Text("這是第一個(gè) tab")),ListTile(title: Text("這是第一個(gè) tab"))],),ListView(children: <Widget>[ListTile(title: Text("這是第一個(gè) tab")),ListTile(title: Text("這是第一個(gè) tab")),ListTile(title: Text("這是第一個(gè) tab"))],),],),),),);} }

把 TabBar 放在導(dǎo)航最頂部

把TabBar放在titile里面

import 'package:flutter/material.dart';class AppBardemoPage extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: DefaultTabController(length: 2,child: Scaffold(appBar: AppBar(isScrollable: true, //如果多個(gè)按鈕的話可以滑動(dòng) // backgroundColor: Colors.red,leading: IconButton(icon: Icon(Icons.arrow_back),tooltip: "Search",onPressed: () {Navigator.of(context).pop();}),title: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly,children: <Widget>[Expanded(flex: 1,child: TabBar(tabs: <Widget>[Tab(text: "熱門"), Tab(text: "推薦")],))],),),body: TabBarView(children: <Widget>[ListView(children: <Widget>[ListTile(title: Text("這是第一個(gè) tab")),ListTile(title: Text("這是第一個(gè) tab")),ListTile(title: Text("這是第一個(gè) tab"))],),ListView(children: <Widget>[ListTile(title: Text("這是第一個(gè) tab")),ListTile(title: Text("這是第一個(gè) tab")),ListTile(title: Text("這是第一個(gè) tab"))],),],),),),);} }

AppBar 中自定義 TabBar 實(shí)現(xiàn) Tabs 的另一種方法。

TabController需要繼承有狀態(tài)組件

import 'package:flutter/material.dart';class AppBardemoPage extends StatefulWidget {@override_AppBardemoPageState createState() => _AppBardemoPageState(); }class _AppBardemoPageState extends State<AppBardemoPage>with SingleTickerProviderStateMixin {TabController _tabController;@overridevoid initState() {// TODO: implement initStatesuper.initState();_tabController = new TabController(vsync: this,length: 2);}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("AppBardemoPage"),bottom: TabBar(controller: this._tabController, //注意tabs: <Widget>[Tab(text: "熱銷"), Tab(text: "推薦"),],),),body: TabBarView(controller: this._tabController, //注意children: <Widget>[Center(child: Text("熱銷")),Center(child: Text("推薦"))],),);} }

Flutter Drawer 側(cè)邊欄

在 Scaffold 組件里面?zhèn)魅?drawer 參數(shù)可以定義左側(cè)邊欄,傳入 endDrawer 可以定義右側(cè)邊欄。側(cè)邊欄默認(rèn)是隱藏的,我們可以通過手指滑動(dòng)顯示側(cè)邊欄,也可以通過點(diǎn)擊按鈕顯示側(cè)邊欄。

return Scaffold(appBar: AppBar(title: Text("Hello Flutter"),),drawer: Drawer(child: Text("左側(cè)邊欄"),),endDrawer: Drawer(child: Text("右側(cè)邊欄"),),);

DrawerHeader

常見屬性:

屬性描述
decoration設(shè)置頂部背景顏色
child配置子元素
padding內(nèi)邊距
margin外邊距
drawer: Drawer(child: Column(children: <Widget>[DrawerHeader(decoration: BoxDecoration(color: Colors.yellow,image: DecorationImage(image: NetworkImage("https://www.itying.com/images/flutter/2.png"),fit: BoxFit.cover)),child: ListView(children: <Widget>[Text("我是一個(gè)頭部")],),),ListTile(title: Text("個(gè)人中心"),leading: CircleAvatar(child: Icon(Icons.people),),),Divider(),ListTile(title: Text("系統(tǒng)設(shè)置"),leading: CircleAvatar(child: Icon(Icons.settings),),),Divider(),],),),

UserAccountsDrawerHeader

屬性描述
decoration設(shè)置頂部背景顏色
accountName賬戶名稱
accountEmail賬戶郵箱
currentAccountPicture用戶頭像
otherAccountsPictures用來設(shè)置當(dāng)前賬戶其他賬戶頭像
margin
drawer: Drawer(child: Column(children: <Widget>[UserAccountsDrawerHeader(accountName: Text("喵喵喵?"),accountEmail: Text("xxx@xxx.com"),currentAccountPicture: CircleAvatar(// 自動(dòng)處理成圓形,不需要再設(shè)置圖片fitbackgroundImage:NetworkImage("https://www.itying.com/images/flutter/3.png"),),decoration: BoxDecoration(color: Colors.yellow,image: DecorationImage(image: NetworkImage("https://www.itying.com/images/flutter/2.png"),fit: BoxFit.cover)),otherAccountsPictures: <Widget>[Image.network("https://www.itying.com/images/flutter/4.png"),Image.network("https://www.itying.com/images/flutter/5.png"),Image.network("https://www.itying.com/images/flutter/6.png"),],),ListTile(title: Text("個(gè)人中心"),leading: CircleAvatar(child: Icon(Icons.people),),),Divider(),ListTile(title: Text("系統(tǒng)設(shè)置"),leading: CircleAvatar(child: Icon(Icons.settings),),),Divider(),],),),

側(cè)邊欄路由跳轉(zhuǎn)

onTap: () {Navigator.of(context).pop();Navigator.pushNamed(context, "/tabBarController");},

按鈕組件介紹

Flutter 里有很多的 Button 組件很多,常見的按鈕組件有:RaisedButton、FlatButton、IconButton、OutlineButton、ButtonBar、FloatingActionButton 等。

RaisedButton :凸起的按鈕,其實(shí)就是 Material Design 風(fēng)格的 Button

FlatButton :扁平化的按鈕

OutlineButton:線框按鈕

IconButton :圖標(biāo)按鈕

ButtonBar:按鈕組

FloatingActionButton:浮動(dòng)按鈕

按鈕組件中的一些屬性

屬性名稱值類型屬性值
onPressedVoidCallback,一般接收一個(gè)方法必填參數(shù),按下按鈕時(shí)觸發(fā)的回調(diào),接收一個(gè)方法,傳 null 表示按鈕禁用,會(huì)顯示禁用相關(guān)樣式
childWidget文本控件
textColorColor文本顏色
colorColor按鈕的顏色
disabledColorColor按鈕禁用時(shí)的顏色
disabledTextColorColor按鈕禁用時(shí)的文本顏色
splashColorColor點(diǎn)擊按鈕時(shí)水波紋的顏色
highlightColorColor點(diǎn)擊(長按)按鈕后按鈕的顏色
elevationdouble陰影的范圍,值越大陰影范圍越大
padding內(nèi)邊距
shape設(shè)置按鈕的形狀
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(10),
) //圓角按鈕
shape: CircleBorder(
side: BorderSide(
color: Colors.white,
)
) //圓形按鈕
class ButtonDemoPage extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("按鈕演示頁面"),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Row(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[RaisedButton(child: Text('普通按鈕'),onPressed: () {print('點(diǎn)擊了');},),SizedBox(width: 20),RaisedButton(child: Text('有顏色的按鈕'),textColor: Colors.white,color: Colors.blue,onPressed: () {print('點(diǎn)擊了');},),SizedBox(width: 20),RaisedButton(child: Text('陰影按鈕'),textColor: Colors.white,color: Colors.blue,elevation: 10,onPressed: () {print('點(diǎn)擊了');},)],),SizedBox(height: 40),Row(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Container(height: 60,width: 200,child: RaisedButton(child: Text('有寬高的按鈕'),textColor: Colors.white,color: Colors.blue,elevation: 10,onPressed: () {print('點(diǎn)擊了');},))],),SizedBox(height: 40),Row(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Expanded(child: Container(height: 60,margin: EdgeInsets.all(20),child: RaisedButton(child: Text('全屏按鈕'),textColor: Colors.white,color: Colors.blue,elevation: 10,onPressed: () {print('點(diǎn)擊了');},),))],),Row(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Expanded(child: Container(height: 60,margin: EdgeInsets.all(20),child: RaisedButton(child: Text('帶圓角的按鈕'),textColor: Colors.white,color: Colors.blue,elevation: 10,shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10),),onPressed: () {print('點(diǎn)擊了');},),))],)],),),);} }

FloatingActionButton 介紹

FloatingActionButton簡稱FAB ,可以實(shí)現(xiàn)浮動(dòng)按鈕,也可以實(shí)現(xiàn)類似閑魚app的地步凸起導(dǎo)航

屬性名稱屬性值
child子視圖,一般為 Icon,不推薦使用文字
tooltipFAB 被長按時(shí)顯示,也是無障礙功能
backgroundColor背景顏色
elevation未點(diǎn)擊的時(shí)候的陰影
hignlightElevation點(diǎn)擊時(shí)陰影值,默認(rèn) 12.0
onPressed點(diǎn)擊事件回調(diào)
shape可以定義 FAB 的形狀等
mini是否是 mini 類型默認(rèn) false

常用表單介紹

Flutter 中常見的表單有 TextField 單行文本框,TextField 多行文本框、CheckBox、Radio、SwitchCheckboxListTile、RadioListTile、SwitchListTile、Slide.

TextField 文本框組件

TextField 表單常見屬性:

屬性描述
maxLines設(shè)置此參數(shù)可以把文本框改為多行文本框
onChanged文本框改變的時(shí)候觸發(fā)的事件
decorationhintText 類似 html 中的 placeholder
border 配置文本框邊框 OutlineInputBorder 配合使用
labelText lable 的名稱
labelStyle 配置 lable 的樣式
obscureTextobscureText
controllercontroller 結(jié)合 TextEditingController()可以配置表單默認(rèn)顯示的內(nèi)容
TextField(maxLines: 10, // obscureText: true,decoration:InputDecoration(hintText: "密碼框",border: OutlineInputBorder()),)d var _username = TextEditingController();@overridevoid initState() {// TODO: implement initStatesuper.initState();_username.text = '這是文本框初始值';} TextField(controller: _username,onChanged: (value) { // print(value);setState(() {this._username.text = value;});},decoration: InputDecoration(hintText: "請輸入你的內(nèi)容",),)

Checkbox、CheckboxListTile 多選框組件

Checkbox 常見屬性:

屬性描述
valuetrue 或者 false
onChanged改變的時(shí)候觸發(fā)的事件
activeColor選中的顏色、背景顏色
checkColor選中的顏色、Checkbox 里面對號的顏色

CheckboxListTile 常見屬性:

屬性描述
valuetrue 或者 false
onChanged改變的時(shí)候觸發(fā)的事件
activeColor選中的顏色、背景顏色
title標(biāo)題
subtitle二級標(biāo)題
secondary配置圖標(biāo)或者圖片
selected選中的時(shí)候文字顏色是否跟著改變
Checkbox(value: _isSelected,onChanged: (v) {print(v);setState(() {this._isSelected = v;});},activeColor: Colors.red,checkColor: Colors.blue,)d CheckboxListTile(value: _isSelected,title: Text("這是一個(gè)標(biāo)題"),subtitle: Text("這是二級標(biāo)題"),onChanged: (v) {setState(() {this._isSelected = v;});},activeColor: Colors.red,secondary:Image.network("https://www.itying.com/images/flutter/1.png"),selected: _isSelected,)

Radio、RadioListTile 單選按鈕組件

Radio 常用屬性:

屬性描述
value單選的值
onChanged改變時(shí)觸發(fā)
activeColor選中的顏色、背景顏色
groupValue選擇組的值

RadioListTile 常用屬性:

屬性描述
valuetrue 或者 false
onChanged改變的時(shí)候觸發(fā)的事件
activeColor選中的顏色、背景顏色
title標(biāo)題
subtitle二級標(biāo)題
secondary配置圖標(biāo)或者圖片
groupValue選擇組的值
int _groupValue=1; Radio(value: 0,onChanged: (v) {setState(() {this._groupValue = v;});},activeColor: Colors.red,groupValue: _groupValue,), Radio(value: 1,onChanged: (v) {setState(() {this._groupValue = v;});},activeColor: Colors.red,groupValue: _groupValue,) int _groupValue = 1;_handelChange(v) {setState(() {_groupValue = v;});}RadioListTile(value: 1,title: Text("nodejs 視頻教程"),subtitle: Text("egg.js 視頻教程"),secondary:Image.network("https://www.itying.com/images/flutter/1.png"),groupValue: _groupValue,onChanged: _handelChange,),Divider(),RadioListTile(value: 0,title: Container(height: 60,child: Text("這是文本"),color: Colors.red,),subtitle: Text("egg.js 視頻教程"),secondary:Image.network("https://www.itying.com/images/flutter/1.png"),groupValue: _groupValue,onChanged: _handelChange,)

開關(guān) Switch

屬性描述
value單選的值
onChanged改變時(shí)觸發(fā)
activeColor選中的顏色、背景顏色

日期和時(shí)間戳

日期轉(zhuǎn)化成時(shí)間戳:

var now = new DateTime.now();print(now.millisecondsSinceEpoch);//單位毫秒,13 位時(shí)間戳

時(shí)間戳轉(zhuǎn)化成日期:

var now = new DateTime.now(); var a=now.millisecondsSinceEpoch; //時(shí)間戳print(DateTime.fromMillisecondsSinceEpoch(a));

第三方庫 date_format 的使用

文檔:https://pub.dev/packages/date_format

調(diào)用自帶日期組件和時(shí)間組件

日期組件:

var _datetime = DateTime.now();_showDatePicker() async {var date = await showDatePicker(context: context,initialDate: _datetime,firstDate: DateTime(1900),lastDate: DateTime(2050));if (date == null) return;print(date);setState(() {_datetime = date;});}

時(shí)間組件:

var _time = TimeOfDay(hour: 9, minute: 20);_showTimePicker() async {var time = await showTimePicker(context: context, initialTime: _time);if (time == null) return;print(time);setState(() {this._time = time;});} Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Row(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[InkWell(child: Row(children: <Widget>[Text("${formatDate(_datetime, [yyyy, '-', mm, '-', dd])}"),Icon(Icons.arrow_drop_down)],),onTap: _showDatePicker,),InkWell(child: Row(children: <Widget>[Text("${this._time.format(context)}"),Icon(Icons.arrow_drop_down)],),onTap: _showTimePicker,)],)],)

調(diào)用自帶日期組件和時(shí)間組件改為中文

http://bbs.itying.com/topic/5cfb2a12f322340b2c90e764

調(diào)用第三方時(shí)間組件

https://pub.dev/packages/flutter_cupertino_date_picker

懶得記了。。。

輪播圖組件

地址:https://pub.dev/packages/flutter_swiper

import 'package:flutter/material.dart'; import 'package:flutter_swiper/flutter_swiper.dart';class SwiperPage extends StatefulWidget {SwiperPage({Key key}) : super(key: key);_SwiperPageState createState() => _SwiperPageState(); }class _SwiperPageState extends State<SwiperPage> {List<Map> list = [{"url": "https://www.itying.com/images/flutter/1.png"},{"url": "https://www.itying.com/images/flutter/2.png"},{"url": "https://www.itying.com/images/flutter/3.png"}];@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('輪播圖組件演示'),),body: Column(children: <Widget>[Container(width: double.infinity,child: AspectRatio(aspectRatio: 16 / 9,child: new Swiper(itemBuilder: (BuildContext context, int index) {return new Image.network(this.list[index]["url"],fit: BoxFit.fill,);},itemCount: list.length,pagination: new SwiperPagination(),autoplay: true,// control: new SwiperControl(),),),)],),);} }

一、AlertDialog

var alertRel = await showDialog(context: context,builder: (context) {return AlertDialog(title: Text("提示!"),content: Text("確定要?jiǎng)h除嗎"),actions: <Widget>[FlatButton(child: Text("取消"),onPressed: () {Navigator.pop(context, 'Cancle');},),FlatButton(child: Text("確定"),onPressed: () {Navigator.pop(context, 'Ok');},)],);});

二、SimpleDialog

var simpleRel = await showDialog(context: context,builder: (BuildContext context) {return SimpleDialog(title: Text("select 單選按鈕框"),children: <Widget>[SimpleDialogOption(child: Text("Option A"),onPressed: () {Navigator.pop(context, 'Option A');},),Divider(),SimpleDialogOption(child: Text("Option B"),onPressed: () {Navigator.pop(context, 'Option B');},),Divider(),SimpleDialogOption(child: Text("Option C"),onPressed: () {Navigator.pop(context, 'Option C');},)],);});

三、showModalBottomSheet

var actionSheet = await showModalBottomSheet(context: context,builder: (builder) {return Container(height: 200, //高度不設(shè)置顯示一半child: Column(children: <Widget>[ListTile(title: Text("分享 A"),onTap: () {Navigator.pop(context, 'A');},),ListTile(title: Text("分享 B"),onTap: () {Navigator.pop(context, 'B');},),ListTile(title: Text("分享 C"),onTap: () {Navigator.pop(context, 'C');},)],),);});

四、showToast

https://pub.dev/packages/fluttertoast

Fluttertoast.showToast(msg: "This is Short Toast",toastLength: Toast.LENGTH_SHORT,timeInSecForIos: 1);

自定義Dialog

自定義 Dialog 對象,需要繼承 Dialog 類,盡管 Dialog 提供了 child 參數(shù)可以用來寫視圖界面,但是往往會(huì)達(dá)不到我們想要的效果,因?yàn)槟J(rèn)的 Dialog 背景框是滿屏的。如果我們想完全定義界面,就需要重寫 build 函數(shù)。

import 'package:flutter/material.dart';class LoadingDialog extends Dialog {final String text;LoadingDialog(this.text);@overrideWidget build(BuildContext context) {// TODO: implement buildreturn new Material( //創(chuàng)建透明層type: MaterialType.transparency, //透明類型 child: new Center(child: Container(width: 300,height: 200,color: Colors.white,child: Column(children: <Widget>[Padding(padding: EdgeInsets.all(10),child: Stack(children: <Widget>[Align(alignment: Alignment.center,child: Text("關(guān)于我們"),),Align(alignment: Alignment.centerRight,child: InkWell(child: Icon(Icons.close),onTap: () {Navigator.pop(context);},),)],),),Divider(),Column(children: <Widget>[Container(height: 40,child: Text(this.text),)],)],),),);} }

定時(shí)器

import 'dart:async';_showTimer(context) {var timer;timer = Timer.periodic(Duration(milliseconds: 1500), (t) {print('執(zhí)行');Navigator.pop(context);t.cancel();});}

定時(shí)器結(jié)合 Dialog

import 'dart:async';import 'package:flutter/material.dart';class LoadingDialog extends Dialog {final String text;LoadingDialog(this.text);_showTimer(context) {var timer;timer = Timer.periodic(Duration(milliseconds: 1500), (t) {print('執(zhí)行');Navigator.pop(context);t.cancel();});}@overrideWidget build(BuildContext context) {_showTimer(context);return Material(//創(chuàng)建透明層type: MaterialType.transparency, //透明類型child: Center(child: Container(width: 300,height: 200,color: Colors.white,child: Column(children: <Widget>[Padding(padding: EdgeInsets.all(10),child: Stack(children: <Widget>[Align(alignment: Alignment.center,child: Text("關(guān)于我們"),),Align(alignment: Alignment.centerRight,child: InkWell(child: Icon(Icons.close),onTap: () {Navigator.pop(context);},),)],),),Divider(),Column(children: <Widget>[Container(height: 40,child: Text(this.text),)],)],),),),);} }

JSON 字符串和 Map 類型的轉(zhuǎn)換(小項(xiàng)目)

(小項(xiàng)目直接轉(zhuǎn),大項(xiàng)目用模型類,筆記在下面)

import 'dart:convert';var mapData = {"name": "張三", "age": "20"}; var strData = '{"name":"張三","age":"20"}'; print(json.encode(mapData)); //Map轉(zhuǎn)換成Json字符串 print(json.decode(strData)); //Json 字符串轉(zhuǎn)化成 Map 類型

使用 http 庫進(jìn)行網(wǎng)絡(luò)請求

請參考官方文檔:https://pub.dev/packages/http

import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:convert';class HomePage extends StatefulWidget {HomePage({Key key}) : super(key: key);_HomePageState createState() => _HomePageState(); }class _HomePageState extends State<HomePage> {String _news = '';//請求數(shù)據(jù)_getData() async {var apiUrl = "http://127.0.0.1:8080/";var result = await http.get(apiUrl);if (result.statusCode == 200) {// print(json.decode(result.body));setState(() {this._news = json.decode(result.body)["msg"];});} else {print(result.statusCode);}}//提交數(shù)據(jù)_postData() async {var apiUrl = "http://127.0.0.1:8080/x";var result = await http.post(apiUrl, body: {'username': '張三', 'age': '20'});if (result.statusCode == 200) {print(json.decode(result.body));} else {print(result.statusCode);}}@overrideWidget build(BuildContext context) {return Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [Text(this._news),RaisedButton(child: Text('Get請求數(shù)據(jù)'),onPressed: _getData,),SizedBox(height: 20),RaisedButton(child: Text('Post提交數(shù)據(jù)'),onPressed: _postData,),SizedBox(height: 20),RaisedButton(child: Text('Get請求數(shù)據(jù)、渲染數(shù)據(jù)演示demo'),onPressed: () {Navigator.pushNamed(context, '/http');},),SizedBox(height: 20),],),);} }

map遍歷,外層套ListView組件

import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:convert';class HttpDemo extends StatefulWidget {HttpDemo({Key key}) : super(key: key);_HttpDemoState createState() => _HttpDemoState(); }class _HttpDemoState extends State<HttpDemo> {List _list = [];@overridevoid initState() {// TODO: implement initStatesuper.initState();this._getData();}_getData() async {var apiUrl = "http://a.itying.com/api/productlist";var result = await http.get(apiUrl);if (result.statusCode == 200) {print(result.body);setState(() {this._list = json.decode(result.body)["result"];/*{"result": [{"_id": "5ac0896ca880f20358495508","title": "精選熱菜","pid": "0", }, {"_id": "5ac089e4a880f20358495509","title": "特色菜","pid": "0",}]}*/});} else {print("失敗${result.statusCode}");}}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("請求數(shù)據(jù)Demo"),),body: this._list.length > 0? ListView(children: this._list.map((value) { //map遍歷,外層套ListView組件return ListTile(title: Text(value["title"]),);}).toList(),): Text("加載中..."));} }

ListView.builder

import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:convert';class HttpDemo extends StatefulWidget {HttpDemo({Key key}) : super(key: key);_HttpDemoState createState() => _HttpDemoState(); }class _HttpDemoState extends State<HttpDemo> {List _list = [];@overridevoid initState() {// TODO: implement initStatesuper.initState();this._getData();}_getData() async {var apiUrl = "http://a.itying.com/api/productlist";var result = await http.get(apiUrl);if (result.statusCode == 200) {print(result.body);setState(() {this._list = json.decode(result.body)["result"];/*{"result": [{"_id": "5ac0896ca880f20358495508","title": "精選熱菜","pid": "0", }, {"_id": "5ac089e4a880f20358495509","title": "特色菜","pid": "0",}]}*/});} else {print("失敗${result.statusCode}");}}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("請求數(shù)據(jù)Demo"),),body: this._list.length > 0? ListView.builder(itemCount: this._lidst.length,itemBuilder: (context, index) {return ListTile(title: Text("${this._list[index]["title"]}"),);},): Text("加載中..."));} }

Dio 庫

dio 是一個(gè)強(qiáng)大的 Dart Http 請求庫,支持 Restful API、FormData、攔截器、請求取消、Cookie 管理、文件上傳/下載、超時(shí)、自定義適配器等…

https://pub.dev/packages/dio

https://github.com/flutterchina/dio/blob/master/README-ZH.md

不做栗子,依官方最新文檔栗子為準(zhǔn)

下拉刷新和上拉分頁

在 Flutter 官方 sdk 中給我們提供了下拉刷新的組件 RefreshIndicator。但是沒有提供上拉分頁加載更多的組件。但是在 Flutter ListView 中有一個(gè)ScrollController 屬性,它就是專門來控制 ListView 滑動(dòng)事件,在這里我們可以根據(jù) ListView 的位置來判斷是否滑動(dòng)到了底部來做加載更多的處理。

Api 接口:http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=1

下拉刷新

@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("請求數(shù)據(jù) Dio Demo"),),body: this._list.length > 0? RefreshIndicator(onRefresh: _onRefresh,child: ListView.builder(itemCount: this._list.length,itemBuilder: (context, index) {return ListTile(title: Text(this._list[index]["title"]));})): Text("加載中..."));}Future<void> _onRefresh() async {print('執(zhí)行刷新');}

上拉分頁加載更多

_scrollController.position.pixels 滾動(dòng)的距離

_scrollController.position.maxScrollExtent 總距離

核心代碼

ScrollController _scrollController = ScrollController(); //listview 的控制器@overridevoid initState() {// TODO: implement initStatesuper.initState();this._getData();//監(jiān)聽滾動(dòng)條事件_scrollController.addListener(() {if (_scrollController.position.pixels >_scrollController.position.maxScrollExtent - 20) {print("滾動(dòng)到了最底部");_getData();}});}@overridevoid dispose() {// TODO: implement disposesuper.dispose();_scrollController.dispose(); //不用了砍掉,提高性能}ListView.builder(itemCount: this._list.length,controller: _scrollController, //注意itemBuilder: (context, index) {})

完整代碼

import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'dart:convert';class NewsPage extends StatefulWidget {@override_NewsPageState createState() => _NewsPageState(); }class _NewsPageState extends State<NewsPage> {ScrollController _scrollController = ScrollController(); //listview 的控制器List _list = [];int _page = 1;bool isLoading = true; //是否正在加載數(shù)據(jù)@overridevoid initState() {// TODO: implement initStatesuper.initState();this._getData();//監(jiān)聽滾動(dòng)條事件_scrollController.addListener(() {if (_scrollController.position.pixels >_scrollController.position.maxScrollExtent - 20) {print("滑動(dòng)到了最底部");_getData();}});}@overridevoid dispose() {// TODO: implement disposesuper.dispose();_scrollController.dispose(); //不用了砍掉,提高性能}_getData() async {String apiUrl ="http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=${this._page}";Response result = await Dio().get(apiUrl);var res = json.decode(result.data)["result"];// print(json.decode(result.data)["result"]);setState(() {this._list.addAll(res);this._page++;});//判斷是否是最后一頁if (res.length < 20) {setState(() {this.isLoading = false;});}}Widget _getMoreWidget() {if (isLoading) {return Center(child: Padding(padding: EdgeInsets.all(10.0),child: Row(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: <Widget>[Text('加載中...',style: TextStyle(fontSize: 16.0),),CircularProgressIndicator(strokeWidth: 1.0,)],),),);} else {return Center(child: Text("--我是有底線的--"),);}}//下拉刷新Future<void> _onRefresh() async {print("執(zhí)行刷新");this._getData();await Future.delayed(Duration(seconds: 3), () {print("refresh");});}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("請求數(shù)據(jù) Dio Demo"),),body: this._list.length > 0? RefreshIndicator(onRefresh: _onRefresh,child: ListView.builder(controller: _scrollController,itemCount: this._list.length,itemBuilder: (context, index) {if (index == this._list.length - 1) {return Column(children: <Widget>[ListTile(title: Text(this._list[index]["title"], maxLines: 1),onTap: () {Navigator.pushNamed(context, '/newsContent');},),Divider(),_getMoreWidget()],);} else {return Column(children: <Widget>[ListTile(title: Text(this._list[index]["title"], maxLines: 1),onTap: () {Navigator.pushNamed(context, '/newsContent');},),Divider()],);}},),): _getMoreWidget(),);} }

解決請求重復(fù)問題

//解決重復(fù)請求的問題bool flag=true;@overridevoid initState() {super.initState();_getData();//監(jiān)聽滾動(dòng)條滾動(dòng)事件_scrollController.addListener((){//_scrollController.position.pixels //獲取滾動(dòng)條滾動(dòng)的高度//_scrollController.position.maxScrollExtent //獲取頁面高度 if(_scrollController.position.pixels>_scrollController.position.maxScrollExtent-20){if(this.flag && this._hasMore){ //如果已經(jīng)請求就不再獲取數(shù)據(jù)_getData();}}});}@overridevoid dispose() {// TODO: implement disposesuper.dispose();_scrollController.dispose(); //不用了砍掉,提高性能}//獲取數(shù)據(jù)_getData() async {// 請求數(shù)據(jù)之前置為falsesetState(() {this.flag=false; });var api =‘url‘;var result = await Dio().get(api);。。。// 數(shù)據(jù)請求完成之后置為truesetState(() {。。。this.flag=true; }); }

滾動(dòng)條回到頂部

//回到頂部 _scrollController.jumpTo(0);

參考代碼

//導(dǎo)航改變的時(shí)候觸發(fā)_subHeaderChange(id) {if (id == 4) {_scaffoldKey.currentState.openEndDrawer();setState(() {this._selectHeaderId = id;});} else {setState(() {this._selectHeaderId = id;this._sort ="${this._subHeaderList[id - 1]["fileds"]}_${this._subHeaderList[id - 1]["sort"]}";//重置分頁this._page = 1;//重置數(shù)據(jù)this._productList = [];//改變sort排序this._subHeaderList[id - 1]['sort'] =this._subHeaderList[id - 1]['sort'] * -1;//回到頂部_scrollController.jumpTo(0);//重置_hasMorethis._hasMore = true;//重新請求this._getProductListData();});}}

實(shí)現(xiàn)一個(gè)簡單的新聞 APP

涉及的 api 接口:

新聞列表: http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=1

新聞詳情:http://www.phonegap100.com/appapi.php?a=getPortalArticle&aid=20

列表頁

import 'package:cc/pages/NewsContent.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'dart:convert';class NewsPage extends StatefulWidget {@override_NewsPageState createState() => _NewsPageState(); }class _NewsPageState extends State<NewsPage> {ScrollController _scrollController = ScrollController(); //listview 的控制器List _list = [];int _page = 1;bool isLoading = true; //是否正在加載數(shù)據(jù)@overridevoid initState() {// TODO: implement initStatesuper.initState();this._getData();//監(jiān)聽滾動(dòng)條事件_scrollController.addListener(() {if (_scrollController.position.pixels >_scrollController.position.maxScrollExtent - 20) {print("滑動(dòng)到了最底部");_getData();}});}_getData() async {String apiUrl ="http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=${this._page}";Response result = await Dio().get(apiUrl);var res = json.decode(result.data)["result"];// print(json.decode(result.data)["result"]);setState(() {this._list.addAll(res);this._page++;});//判斷是否是最后一頁if (res.length < 20) {setState(() {this.isLoading = false;});}}Widget _getMoreWidget() {if (isLoading) {return Center(child: Padding(padding: EdgeInsets.all(10.0),child: Row(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: <Widget>[Text('加載中...',style: TextStyle(fontSize: 16.0),),CircularProgressIndicator(strokeWidth: 1.0,)],),),);} else {return Center(child: Text("--我是有底線的--"),);}}//下拉刷新Future<void> _onRefresh() async {print("執(zhí)行刷新");this._getData();await Future.delayed(Duration(seconds: 3), () {print("refresh");});}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("請求數(shù)據(jù) Dio Demo"),),body: this._list.length > 0? RefreshIndicator(onRefresh: _onRefresh,child: ListView.builder(controller: _scrollController,itemCount: this._list.length,itemBuilder: (context, index) {if (index == this._list.length - 1) {return Column(children: <Widget>[ListTile(title: Text(this._list[index]["title"], maxLines: 1),onTap: () {Navigator.pushNamed(context, '/newsContent');},),Divider(),_getMoreWidget()],);} else {return Column(children: <Widget>[ListTile(title: Text(this._list[index]["title"], maxLines: 1),onTap: () {Navigator.of(context).push(MaterialPageRoute(builder: (context) =>NewsContent(this._list[index]["aid"])));},),Divider()],);}},),): _getMoreWidget(),);} }

詳情頁

import 'package:flutter/material.dart'; import 'dart:convert'; import 'package:dio/dio.dart';class NewsContent extends StatefulWidget {var aid;NewsContent(this.aid);createState() => _NewsContentState(); }class _NewsContentState extends State<NewsContent> {List list = [];_getData() async {String apiUrl ="http://www.phonegap100.com/appapi.php?a=getPortalArticle&aid=${this.widget.aid}";Response response = await Dio().get(apiUrl);setState(() {this.list = json.decode(response.data)["result"];});}@overridevoid initState() {// TODO: implement initStatesuper.initState();this._getData();}@overrideWidget build(BuildContext context) {return Container(child: Scaffold(appBar: AppBar(title: Text(this.list.length > 0 ? this.list[0]["title"] : ""),),body: ListView(children: <Widget>[Text(this.list.length > 0 ? this.list[0]["content"] : "")],),),);} }

解析 html

https://pub.dev/packages/flutter_html

雞肋。。。只能解析部分HTML標(biāo)簽

WebView 組件 inappbrowser的使用

建議使用WebView_flutter官方庫,inappbrowser翹辮子了

https://pub.dev/packages/flutter_inappbrowser

ios模擬器測試失敗。。。

獲取設(shè)備信息

https://pub.dev/packages/device_info

使用高德定位準(zhǔn)備工作獲取 key

1、申請成為開發(fā)者

2、創(chuàng)建應(yīng)用配置獲取 Key (參考教程演示)

https://lbs.amap.com/api/android-sdk/guide/create-project/get-key

實(shí)現(xiàn)用高德定位

https://pub.dev/packages/amap_location

image_picker 實(shí)現(xiàn)相機(jī)拍照和相冊選擇

https://pub.dev/packages/image_picker

/*拍照*/_takePhoto() async {var image = await ImagePicker.pickImage(source: ImageSource.camera);setState(() {_imgPath = image;});}/*相冊*/_openGallery() async {var image = await ImagePicker.pickImage(source: ImageSource.gallery);setState(() {_imgPath = image;});}

上傳圖片到服務(wù)器

https://pub.dev/packages/dio

Dio2.x和3.x代碼不同,依Dio官方文檔為準(zhǔn)

//上傳圖片_uploadImage(File _imageDir) async {//注意:dio3.x版本為了兼容web做了一些修改,上傳圖片的時(shí)候需要把File類型轉(zhuǎn)換成String類型,具體代碼如下var fileDir = _imageDir.path;FormData formData = FormData.fromMap({"name": "zhangsna 6666666666","age": 20,"sex": "男","file": await MultipartFile.fromFile(fileDir, filename: "xxx.jpg")});var response =await Dio().post("http://jd.itying.com/imgupload", data: formData);print(response);}

視頻播放

video_player 官方庫,支持flutter web

在 Flutter 里官方提供了一個(gè) video_player 插件可以播放視頻。但是 video_player 有一些局限性。沒法控制底部播放進(jìn)度等。 所以我們主要給大家講解一個(gè)第三方的視頻播放庫chewie。chewie 是一個(gè)非官方的第三方視頻播放組件,看起來好像是基于 HTML5 播放的組件。chewie 相對 video_player 來說,有控制欄和全屏的功能。Chewie 使用 video_player 引擎并將其包裹在友好的 Material 或 Cupertino UI 中!

https://pub.dev/packages/video_player

https://pub.dev/packages/chewie

iOS警告

chewie使用的視頻播放器插件在iOS模擬器上不起作用。開發(fā)/測試期間必須使用iOS設(shè)備。

coffee 21:44:59
安利一下 flutter_ijkplayer 視頻播放器,這兩天試了很多個(gè),還是這個(gè)解決了目前的問題

coffee 22:31:46
flutter_tencentplayer 騰訊云的ios無法播放rtmp協(xié)議的直播流
coffee 22:33:13
官方提供的video_player 沒有ui,需要自己實(shí)現(xiàn)
coffee 22:34:47
chewie 包裝了一層video_player提供了ui,我視頻回放用的這個(gè),費(fèi)了好大力氣才搞定使用原視頻尺寸播放,今兒發(fā)現(xiàn) flutter_ijkplayer 很好用,不過回放功能暫時(shí)不打算改了,好不容易調(diào)完

chewie 視頻播放完整 demo

import 'package:flutter/material.dart'; import 'package:chewie/chewie.dart'; import 'package:video_player/video_player.dart';class ChewieVideoDemo extends StatefulWidget {ChewieVideoDemo({Key key}) : super(key: key);_ChewieVideoDemoState createState() => _ChewieVideoDemoState(); }class _ChewieVideoDemoState extends State<ChewieVideoDemo> {VideoPlayerController videoPlayerController;ChewieController chewieController;@overridevoid initState() { // TODO: implement initState super.initState();videoPlayerController = VideoPlayerController.network('http://vfx.mtime.cn/Video/2019/02/04/mp4/190204084208765161.mp4');chewieController = ChewieController(videoPlayerController: videoPlayerController,aspectRatio: 3 / 2,autoPlay: true,looping: true,);}@overridevoid dispose() { // TODO: implement dispose super.dispose();videoPlayerController.dispose();chewieController.dispose();super.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('標(biāo)題'),),body: Center(child: Chewie(controller: chewieController,),),);} }

檢測網(wǎng)絡(luò)

https://pub.dev/packages/connectivity

檢測網(wǎng)絡(luò)完整 demo

import 'package:flutter/material.dart'; import 'package:connectivity/connectivity.dart';class NetworkPage extends StatefulWidget {NetworkPage({Key key}) : super(key: key);_NetworkPageState createState() => _NetworkPageState(); }class _NetworkPageState extends State<NetworkPage> {String _state;var _subscription;@overrideinitState() {super.initState();_subscription = Connectivity().onConnectivityChanged.listen((ConnectivityResult result) {// Got a new connectivity status!if (result == ConnectivityResult.mobile) {setState(() {_state = "手機(jī)網(wǎng)絡(luò)";}); // I am connected to a mobile network. } else if (result == ConnectivityResult.wifi) {setState(() {_state = "Wifi 網(wǎng)絡(luò)";}); // I am connected to a wifi network. }else{setState(() {_state = "沒有網(wǎng)絡(luò)";});}});}@overridedispose() {super.dispose();_subscription.cancel();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("檢測網(wǎng)絡(luò)變化"),),body: Text("${_state}"),);} }

本地存儲(chǔ)

https://pub.dev/packages/shared_preferences

注意:

如果SharedPreferences prefs = await SharedPreferences.getInstance();寫在runapp()的外層,

要加上

本地存儲(chǔ)里面常用的一些方法

1、設(shè)置值

SharedPreferences prefs = await SharedPreferences.getInstance(); prefs.setString(key, value); prefs.setBool(key, value) prefs.setDouble(key, value) prefs.setInt(key, value) prefs.setStringList(key, value)

2、獲取值

SharedPreferences prefs = await SharedPreferences.getInstance(); var data=prefs.getString("name");

3、刪除值

SharedPreferences prefs = await SharedPreferences.getInstance(); prefs.remove(key); //刪除指定鍵 prefs.clear();//清空鍵值對

本地存儲(chǔ)完整 demo

import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart';class StoragePage extends StatefulWidget {StoragePage({Key key}) : super(key: key);_StoragePageState createState() => _StoragePageState(); }class _StoragePageState extends State<StoragePage> {_saveData() async {SharedPreferences prefs = await SharedPreferences.getInstance();prefs.setString("name", "張三"); // prefs.setBool(key, value) // prefs.setDouble(key, value)// prefs.setInt(key, value) // prefs.setStringList(key, value)}_getData() async {SharedPreferences prefs = await SharedPreferences.getInstance();var data = prefs.getString("name");print(data);}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("本地存儲(chǔ)"),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [RaisedButton(child: Text('保存數(shù)據(jù)'),onPressed: _saveData,),SizedBox(height: 10),RaisedButton(child: Text('獲取數(shù)據(jù)'),onPressed: _getData,)]),),);} }

掃描二維碼條形碼插件

https://pub.dev/packages/barcode_scan

錯(cuò)誤多多,修改大大,不做記錄。。。用到再說

檢測應(yīng)用版本號、服務(wù)器下載文件以及實(shí)現(xiàn) App 自動(dòng)升級、安裝

1、Android App 升級執(zhí)行流程

1、獲取本地版本號

2、請求服務(wù)器獲取服務(wù)器版本號

3、本地版本和服務(wù)器版本不一致提示升級,彈窗提示用戶是否更新

4、用戶確定升級,調(diào)用文件傳輸方法下載 apk 文件

5、監(jiān)聽下載進(jìn)度

6、下載完成打開 Apk 進(jìn)行安裝

注意:在 Ios 中沒法直接下載安裝,如果版本不一致直接跳轉(zhuǎn)到 Ios 應(yīng)用對應(yīng)的應(yīng)用市場就可以了。

配置版本號: (Flutter應(yīng)用獲取的不是這里的版本號,在pubspec.yaml文件)

<manifest android:hardwareAccelerated="true" android:versionCode="1" android:versionName="0.0.1" package="io.jdshop.demo" xmlns:android="http://schemas.android.com/apk/res/android">

2、升級 app 之前的準(zhǔn)備工作配置權(quán)限

配置 AndroidMenifest.xml 文件

<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

3、Android 升級 app 涉及的 API 庫

插件名稱描述插件地址
package_info檢測版本號https://pub.dev/packages/package_info
path_provider獲取文件存儲(chǔ)路徑https://pub.dev/packages/path_provider
flutter_downloaderflutter_downloaderhttps://pub.dev/packages/flutter_downloader
open_file打開文件插件https://pub.dev/packages/open_file

4、獲取版本信息

https://pub.dev/packages/package_info

PackageInfo packageInfo = await PackageInfo.fromPlatform(); String appName = packageInfo.appName; String packageName = packageInfo.packageName; String version = packageInfo.version; String buildNumber = packageInfo.buildNumber; print("appName:${appName}"); print("packageName:${packageName}"); print("version:${version}"); print("buildNumber:${buildNumber}");

5、獲取文件存儲(chǔ)路徑

https://pub.dev/packages/path_provider

Directory tempDir = await getTemporaryDirectory(); String tempPath = tempDir.path;Directory appDocDir = await getApplicationDocumentsDirectory(); String appDocPath = appDocDir.path;var directory = await getExternalStorageDirectory();String storageDirectory=directory.path;print("tempPath:${tempPath}");print("appDocDir:${appDocPath}");print("StorageDirectory:${storageDirectory}");

6、下載文件

https://pub.dev/packages/flutter_downloader

final directory = await getExternalStorageDirectory();String _localPath = directory.path;final taskId = await FlutterDownloader.enqueue(url: "http://www.ionic.wang/jdshop.apk",savedDir: _localPath,showNotification:true, // show download progress in status bar (for Android)openFileFromNotification:true, // click on notification to open downloaded file (for Android)

7、打開文件

https://pub.dev/packages/open_file

OpenFile.open("${_localPath}/jdshop.apk");

8、注意事項(xiàng)

1、服務(wù)器的 App 版本必須大于本地 App 版本

2、本地 App 和服務(wù)器 App 的包名稱 簽名必須一致,這樣的話服務(wù)器的包才可以替換本地的包。

完整代碼

import 'package:flutter/material.dart';import 'package:package_info/package_info.dart';import 'package:path_provider/path_provider.dart'; import 'dart:io';import 'package:open_file/open_file.dart';import 'package:flutter_downloader/flutter_downloader.dart';class AppVersionPage extends StatefulWidget {AppVersionPage({Key key}) : super(key: key);_AppVersionPageState createState() => _AppVersionPageState(); }class _AppVersionPageState extends State<AppVersionPage> {@overridevoid initState() {// TODO: implement initStatesuper.initState();this._getPackageInfo();this._getAppPath();}//彈出Dialog_showDialog() async {var alertRel = await showDialog(context: context,builder: (context) {return AlertDialog(title: Text("更新APP提示!"),content: Text("發(fā)現(xiàn)新的版本,新版本修復(fù)了如下bug 是否更新!"),actions: <Widget>[FlatButton(child: Text("否"),onPressed: () {Navigator.pop(context, 'Cancle');},),FlatButton(child: Text("是"),onPressed: () {Navigator.pop(context, 'Ok');},)],);});}//獲取版本號_getPackageInfo() async {PackageInfo packageInfo = await PackageInfo.fromPlatform();String appName = packageInfo.appName;String packageName = packageInfo.packageName;String version = packageInfo.version;String buildNumber = packageInfo.buildNumber;print("appName:${appName}");print("packageName:${packageName}");print("version:${version}");print("buildNumber:${buildNumber}");}//獲取路徑_getAppPath() async {Directory tempDir = await getTemporaryDirectory();String tempPath = tempDir.path;Directory appDocDir = await getApplicationDocumentsDirectory();String appDocPath = appDocDir.path;var directory = await getExternalStorageDirectory();String storageDirectory = directory.path;print("tempPath:${tempPath}");print("appDocDir:${appDocPath}");print("StorageDirectory:${storageDirectory}");}//下載打開文件_downLoad() async {final directory = await getExternalStorageDirectory();String _localPath = directory.path;final taskId = await FlutterDownloader.enqueue(url: "http://www.ionic.wang/jdshop.apk",savedDir: _localPath,showNotification:true, // show download progress in status bar (for Android)openFileFromNotification:true, // click on notification to open downloaded file (for Android));FlutterDownloader.registerCallback((id, status, progress) {print(status);// code to update your UIprint('1111111');print(progress);});//打開文件OpenFile.open("${_localPath}/jdshop.apk");}@overrideWidget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: Icon(Icons.arrow_downward),onPressed: _downLoad,),appBar: AppBar(title: Text("app升級演示"),),body: Text("app升級演示"),);} }

調(diào)用 url_launcher 模塊打開外部瀏覽器 打開外部應(yīng)用 撥打電話 發(fā)送短信

1、Flutter url_launcher 模塊

Flutter url_launcher 模塊可以讓我們實(shí)現(xiàn)打開外部瀏覽器、打開外部應(yīng)用、發(fā)送短信、撥打電話等功能。

https://pub.dev/packages/url_launcher

2、Flutter url_launcher 模塊的使用

import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart';class _UrlLauncherState extends State<UrlLauncher> {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('UrlLauncher'),),body: Center(child: Padding(padding: EdgeInsets.all(20),child: ListView(children: <Widget>[RaisedButton(child: Text('打開外部瀏覽器'),onPressed: () async {const url = 'https://cflutter.com';if (await canLaunch(url)) {await launch(url);} else {throw 'Could not launch $url';}},),SizedBox(height: 10,),RaisedButton(child: Text('撥打電話'),onPressed: () async {const tel = 'tel:10086';if (await canLaunch(tel)) {await launch(tel);} else {throw 'Could not launch $tel';}},),SizedBox(height: 10),RaisedButton(child: Text('發(fā)送短信'),onPressed: () async {const tel = 'sms:10086';if (await canLaunch(tel)) {await launch(tel);} else {throw 'Could not launch $tel';}},),SizedBox(height: 10),RaisedButton(child: Text('打開外部應(yīng)用'),onPressed: () async {// weixin://const app = 'alipays://';if (await canLaunch(app)) {await launch(app);} else {throw 'Could not launch $app';}},)],),),),);} }

3、關(guān)于打開其他 app 請參考這個(gè)帖子

https://www.cflutter.com/topic/5d0853733b57e317a4d0af01

Android 修改應(yīng)用名稱、應(yīng)用圖標(biāo)、應(yīng)用啟動(dòng)畫面

1、Android 修改應(yīng)用名稱

Android 是在 android ? app ? src ? main? AndroidManifest.xml 中修改 android:label=“XXX”;

2、Android 修改應(yīng)用圖標(biāo)

Android 在 android ? app ? src ? res ? mipmap 下面對應(yīng)的文件夾中替換相應(yīng)圖片

3、Android 修改應(yīng)用啟動(dòng)畫面

Android 添加啟動(dòng)界面

打開文件 android/app/src/main/res/drawable/launch_background.xml

修改內(nèi)容,打開注釋了的代碼 launch_image 那段。

<!-- <item><bitmapandroid:gravity="center"android:src="@mipmap/launch_image" /></item> -->

里面的ic_launch.png是圖標(biāo),啟動(dòng)畫面添加launch_image.png,格式要求png

注意@mipmap/launch_image 就是你要設(shè)置的啟動(dòng)界面的圖片資源名字,你要放置到對應(yīng)的文件夾里面

密度代表分辨率
ldpi240 x 320
mdpi320 x 480
hdpi480 x 800
xhdpi720 x 1280
xxhdpi1080 x 1920
xxxhdpi3840×2160

豎向 ListView 嵌套橫向 ListView ,以及ListView 嵌套 GridView

1、豎向 ListView 嵌套橫向 ListView 注意事項(xiàng):

在豎向 ListView 中嵌套橫向 ListView 的時(shí)候要注意給橫向 ListView 外層加一個(gè)容器,然后外層這個(gè)容器要設(shè)置高度,外層這個(gè)容器可以是 SizedBox ,也可以是 Container。

2、ListView 嵌套 GridView 注意事項(xiàng):

由于 GridView 和 ListView 都是可以滾動(dòng)的組件,所以嵌套的時(shí)候要注意把里面的組件改為不可滾動(dòng)組件。

重要屬性:

shrinkWrap: true, //解決無限高度問題

physics:NeverScrollableScrollPhysics(), //禁用滑動(dòng)事件

不同終端屏幕適配問題

我寫的代碼還用適配???

JSON 序列化反序列化(模型類)

1、使用 dart:convert 手動(dòng)序列化 JSON

2、模型類中序列化 JSON

小項(xiàng)目中使用 dart:convert 手動(dòng)序列化 JSON 非常好,也非常快速。但是隨著項(xiàng)目的增大,dart:convert 手動(dòng)序列化 JSON 的話失去了大部分靜態(tài)類型語言特性:類型安全、自動(dòng)補(bǔ)全和最重要的編譯時(shí)異常。這樣一來,我們的代碼可能會(huì)變得非常容易出錯(cuò)。

當(dāng)我們訪問 name 或 email 字段時(shí),我們輸入的很快,導(dǎo)致字段名打錯(cuò)了。但由于這個(gè) JSON 在 map 結(jié)構(gòu)中,所以編譯器不知道這個(gè)錯(cuò)誤的字段名。

為了解決上面的問題在大型項(xiàng)目中使用的更多的是在模型類中序列化 JSON。

JSON字符串和Map類型的轉(zhuǎn)換 dart:convert手動(dòng)序列化 JSON

import 'dart:convert';var mapData = {"name": "張三", "age": "20"};var strData = '{"name":"張三","age":"20"}';print(json.encode(mapData)); //Map轉(zhuǎn)換成Json字符串print(json.decode(strData)); //Json 字符串轉(zhuǎn)化成 Map 類型

在模型類中序列化 JSON

class FocusModel {String sId;String title;String status;String pic;String url;FocusModel({this.sId, this.title, this.status, this.pic, this.url});FocusModel.fromJson(Map<String, dynamic> json) {sId = json['_id'];title = json['title'];status = json['status'];pic = json['pic'];url = json['url'];}Map<String, dynamic> toJson() {final Map<String, dynamic> data = new Map<String, dynamic>();data['_id'] = this.sId;data['title'] = this.title;data['status'] = this.status;data['pic'] = this.pic;data['url'] = this.url;return data;} } var strData='{"_id":"59f6ef443ce1fb0fb02c7a43","title":"筆記本電腦","status":"1","pic":"public\\upload\\UObZahqPYzFvx_C9CQjU8KiX.png","url":"12"}';var data=FocusModel.fromJson(strData);

可參考:https://flutterchina.club/json/

json_to_dart 自動(dòng)生成模型類

https://javiercbk.github.io/json_to_dart/

IndexedStack 保持頁面狀態(tài)

IndexedStack 和 Stack 一樣,都是層布局控件, 可以在一個(gè)控件上面放置另一個(gè)控件,但唯一不同的是 IndexedStack 在同一時(shí)刻只能顯示子控件中的一個(gè)控件,通過 Index 屬性來設(shè)置顯示的控件。

IndexedStack 來保持頁面狀態(tài)的優(yōu)點(diǎn)就是配置簡單。IndexedStack 保持頁面狀態(tài)的缺點(diǎn)就是不方便單獨(dú)控制每個(gè)頁面的狀態(tài)。

body: IndexedStack(index: this._currentIndex,children: <Widget>[],),

AutomaticKeepAliveClientMixin 保持頁面狀態(tài)

花里胡哨。。。

通過事件打開側(cè)邊欄

final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>(); return Scaffold( key:_scaffoldKey, appBar: AppBar( title: Text("商品列表"), ) ) Expanded( flex: 1, child: InkWell( onTap: () { _scaffoldKey.currentState.openEndDrawer(); }, child: Text("篩選", textAlign: TextAlign.center), ), )

修改主題樣式

return MaterialApp( debugShowCheckedModeBanner: false, // home: Tabs(), initialRoute: '/', onGenerateRoute:onGenerateRoute, theme: ThemeData( primaryColor: Colors.white ), );

下拉菜單 showMenu

IconButton(icon: Icon(Icons.more_horiz),onPressed: () {showMenu(context: context,position: RelativeRect.fromLTRB(500, 76, 10, 0),items: [PopupMenuItem(child: Row(children: <Widget>[Icon(Icons.home),Container(padding: EdgeInsets.fromLTRB(20, 0, 20, 0),child: Text("首頁"),)],),),PopupMenuItem(child: Row(children: <Widget>[Icon(Icons.search),Container(padding: EdgeInsets.fromLTRB(20, 0, 20, 0),child: Text("搜索"),)],),)]);})

彈出底部菜單

實(shí)際就是點(diǎn)擊事件后,彈出showModalBottomSheet,參考diolog筆記

StatefulBuilder更新Flutter showDialog 、showModalBottomSheet 中的狀態(tài)

參考:https://www.cflutter.com/topic/5d202202403aa10564178c65

狀態(tài)管理

通俗的講:當(dāng)我們想在多個(gè)頁面(組件/Widget)之間共享狀態(tài)(數(shù)據(jù)),或者一個(gè)頁面(組件/Widget)中的多個(gè)子組件之間共享狀態(tài)(數(shù)據(jù)),這個(gè)時(shí)候我們就可以用 Flutter 中的狀態(tài)管理來管理統(tǒng)一的狀態(tài)(數(shù)據(jù)),實(shí)現(xiàn)不同組件直接的傳值和數(shù)據(jù)共享。

現(xiàn)在 Flutter 的狀態(tài)管理方案很多,redux、bloc、state、provide、provider。

目前我們推薦使用 provider,這個(gè)是官方提供的狀態(tài)管理解決方案。相比其他狀態(tài)管理庫使用起來比較方便。

provider庫和flutter provide庫

provider 是 Flutter 團(tuán)隊(duì)推出的狀態(tài)管理模式。

官方地址為:https://pub.dev/packages/provider

注意:provider 和 provide 是兩個(gè)庫哦。Flutter 官方推薦使用的是 provider 哦,provider 是flutter 官方出的。provide 不是 Flutter 官方寫的哦。

provider 的使用

(官方文檔為準(zhǔn),builder關(guān)鍵字變creat)

1、配置依賴 provider: ^4.3.3
2、新建一個(gè)文件夾叫 provider,在 provider 文件夾里面放我們對于的狀態(tài)管理類

3、在 provider 里面新建 Counter.dart
4、Counter.dart 里面新建一個(gè)類繼承 minxins 的 ChangeNotifier 代碼如下

import 'package:flutter/material.dart';class Counter with ChangeNotifier {int _count = 0;int get count => _count;void increment() {_count++;notifyListeners();} }

5、找到 main.dart 修改代碼如下

import 'package:flutter/material.dart'; import 'routers/router.dart'; import 'package:provider/provider.dart'; import 'provider/Counter.dart';void main() => runApp(MyApp());// void main() => runApp(MyApp()); class MyApp extends StatefulWidget {MyApp({Key key}) : super(key: key);_MyAppState createState() => _MyAppState(); }class _MyAppState extends State<MyApp> {@overrideWidget build(BuildContext context) {return MultiProvider(providers: [// Provider<Counter>.value(value: foo),ChangeNotifierProvider(create: (_) => Counter()), // provider4.x的寫法全局監(jiān)聽//ChangeNotifierProvider(builder: (_) => Counter()),],child: MaterialApp(// home: Tabs(),debugShowCheckedModeBanner: false,initialRoute: '/productContent',onGenerateRoute: onGenerateRoute,theme: ThemeData(// primaryColor: Colors.yellowprimaryColor: Colors.white),));} }

6、獲取值、以及設(shè)置值

import 'package:provider/provider.dart'; import '../../provider/Counter.dart';Widget build(BuildContext context) {final counter = Provider.of<Counter>(context); // counter.init();//在build里面return Scaffold(floatingActionButton: FloatingActionButton(child: Icon(Icons.add),onPressed: () {counter.increment();},),body: Text("counter 的值:${counter.count}")); }

擴(kuò)展栗子

import 'package:flutter/material.dart';class Cart with ChangeNotifier {List _cartList = []; //狀態(tài)// int _cartNum=0; //數(shù)量直接從數(shù)組中獲取,不用在定義一個(gè)獲取數(shù)量的方法int get cartNum => this._cartList.length; //數(shù)量直接從數(shù)組中獲取,不用在定義一個(gè)獲取數(shù)量的方法List get cartList => this._cartList;addData(value) {this._cartList.add(value);notifyListeners();}deleteData(value) {this._cartList.remove(value);notifyListeners();} }

event_bus 事件廣播 事件監(jiān)聽

花里胡哨。。。

MediaQuery.removePadding移除元素的pandding

通過MediaQuery.removePadding可以移除元素的pandding,需要注意要指定移除哪個(gè)方向的padding,例如移除上面的padding

MediaQuery.removePadding(removeTop: true,context: context,child: , )

瀑布流布局

https://pub.dev/packages/flutter_staggered_grid_view

new StaggeredGridView.countBuilder(crossAxisCount: 4,itemCount: 8,itemBuilder: (BuildContext context, int index) => new Container(color: Colors.green,child: new Center(child: new CircleAvatar(backgroundColor: Colors.white,child: new Text('$index'),),)),staggeredTileBuilder: (int index) =>new StaggeredTile.count(2, index.isEven ? 2 : 1), //固定個(gè)數(shù)修改count()為fit(2)mainAxisSpacing: 4.0,crossAxisSpacing: 4.0, )

Sliver牛逼!!!

return Scaffold(body: CustomScrollView(slivers: <Widget>[SliverAppBar( // title: Text("SliverAppBar"), // pinned: true,floating: true,expandedHeight: 200,flexibleSpace: FlexibleSpaceBar(title: Text("Hello Flutter".toUpperCase()),background: Image.network("https://images.unsplash.com/photo-1579964190836-13f5022f5c40?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",fit: BoxFit.cover,),),),SliverGrid(delegate: SliverChildBuilderDelegate((context, index) {return Container(child: Center(child: Text("$index"),),);}, childCount: 1000),gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2,crossAxisSpacing: 8.0,mainAxisSpacing: 8.0))],),);

適配夜間模式

簡單做法:

直接設(shè)置模式

theme: ThemeData.dark(),

自動(dòng)切換模式

darkTheme: ThemeData.dark()

正片:

夜間模式(Dark Mode),也被稱為暗黑模式或深色模式,是一種高對比度,或者反色模式的顯示模式,開啟之后在夜間可以緩解疲勞,更易于閱讀,同時(shí)也能在一定程度上達(dá)到省電的效果。

夜間模式跟隨系統(tǒng)

使用MaterialApp的darkTheme選項(xiàng),可以很方便地適配跟隨系統(tǒng)的DarkMode:

MaterialApp( theme: ThemeData( brightness: Brightness.light, primaryColor: Colors.blue, ), darkTheme: ThemeData( brightness: Brightness.dark, ), );

也可直接寫做

darkTheme: ThemeData.dark()
  • 這種方式是自動(dòng)跟隨iOS/Android的系統(tǒng)設(shè)置來切換的,無需用戶再單獨(dú)設(shè)置

手動(dòng)開啟夜間模式

上述的跟隨系統(tǒng)自動(dòng)切換暗黑模式的體驗(yàn)可能并不是很好,比如用戶不喜歡夜間模式或者App的夜間模式配色適配并不是很好,這就會(huì)導(dǎo)致用戶無法手動(dòng)控制app的夜間模式或者只能關(guān)閉系統(tǒng)的設(shè)置。因此我們可以增加手動(dòng)控制以及跟隨系統(tǒng)的選項(xiàng),讓用戶選擇是否開啟以及開啟的方式。

保存用戶配置

在flutter中可以使用shared_preferences來保存用戶的配置數(shù)據(jù),具體使用方法詳見:shared_preferences使用

狀態(tài)管理

主題的手動(dòng)切換是影響全局的,如果通過常規(guī)的數(shù)據(jù)流向很難做到。常見的幾種狀態(tài)管理:

  • InheritedWidget
  • Scoped model
  • BLoC
  • Redux
  • Provider

Provider是Google I/O 2019大會(huì)宣布的現(xiàn)在官方推薦的狀態(tài)管理方式,我們需要在設(shè)置頁里面通過用戶設(shè)置,把變更狀態(tài)共享給其他Widget,這里采用Provider這種方式來實(shí)現(xiàn)狀態(tài)共享。

通用夜間模式Provider Model類

import 'package:flutter/foundation.dart'; import 'package:shared_preferences/shared_preferences.dart';class DarkModeModel with ChangeNotifier {/// 夜間模式 0: 關(guān)閉 1: 開啟 2: 隨系統(tǒng)int _darkMode;static const Map<int, String> darkModeMap = {0: "關(guān)閉",1: "開啟",2: "跟隨系統(tǒng)"};static const String STORE_KEY = 'darkMode';SharedPreferences _prefs;int get darkMode => _darkMode;DarkModeModel() {_init();}void _init() async {this._prefs = await SharedPreferences.getInstance();int localMode = this._prefs.getInt(STORE_KEY);changeMode(localMode ?? 0);}void changeMode(int darkMode) async {_darkMode = darkMode;notifyListeners();SharedPreferences prefs = this._prefs ?? SharedPreferences.getInstance();await prefs.setInt(STORE_KEY, darkMode);} }

MaterialApp修改

如果手動(dòng)控制是否開啟夜間模式,可以設(shè)置MaterialApp的theme選項(xiàng)為ThemeData.dark()

theme: ThemeData.dark()

因?yàn)樾枰瑫r(shí)保留隨系統(tǒng)自動(dòng)切換與手動(dòng)切換,而darkTheme選項(xiàng)和theme又有沖突,所以這里需要根據(jù)darkModeModel.darkMode的取值來渲染不同的MaterialApp,如果是手動(dòng)模式再根據(jù)darkModeModel.darkMode的取值來渲染不同的theme。

MultiProvider(providers: [ChangeNotifierProvider(builder: (_) => DarkModeModel())],child: Consumer<DarkModeModel>(builder: (context, darkModeModel, _) {return darkModeModel.darkMode == 2 ? MaterialApp(title: '特效君',theme: ThemeData(primarySwatch: Colors.blue,),darkTheme: ThemeData.dark(),home: MainPage(title: '特效君'),) : MaterialApp(title: '特效君',theme: darkModeModel.darkMode == 1 ? ThemeData.dark(): ThemeData(primarySwatch: Colors.blue,),home: MainPage(title: '特效君'),);},), )

這樣我們就可以給用戶提供自動(dòng)跟隨系統(tǒng)切換以及手動(dòng)控制的選項(xiàng)了

登錄注冊案例

login.dart

class Login extends StatefulWidget {@override_LoginState createState() => _LoginState(); }class _LoginState extends State<Login> {String _nickname = "";String _password = "";final _formKey = GlobalKey<FormState>();bool _autoValidate = false;_toLogin() async {Response response = await Dio().post("http://127.0.0.1:8080/auth",data: {"nickname": _nickname, "password": _password});LoginModel signUp = LoginModel.fromJson(response.data);if (signUp.code == 2000) {Scaffold.of(context).showSnackBar(SnackBar(content: Text(signUp.msg)));} else {Scaffold.of(context).showSnackBar(SnackBar(content: Text(signUp.msg)));}}void _submitForm() {if (_formKey.currentState.validate()) {_formKey.currentState.save();_toLogin();} else {setState(() {_autoValidate = true;});}}String _validateNickname(String value) {if (value.isEmpty) {return "<昵稱>不能為空!";} else if (value.length > 20) {return "<昵稱>不能大于20個(gè)字符!";}return null;}String _validatePassword(String value) {if (value.isEmpty) {return "<密碼>不能為空!";} else if (value.length < 6) {return "<密碼>不能小于6位!";}return null;}@overrideWidget build(BuildContext context) {return Scaffold(body: GestureDetector(behavior: HitTestBehavior.translucent,onTap: () {FocusScope.of(context).requestFocus(FocusNode());},// 外層包一個(gè)Container方便設(shè)置內(nèi)外邊距,背景圖片等child: Container(padding: EdgeInsets.all(40.0),child: ListView(children: <Widget>[// 登錄LOGOContainer(height: 100,decoration: BoxDecoration(image: DecorationImage(image: NetworkImage("https://c-ssl.duitang.com/uploads/item/201607/23/20160723143004_vyjTa.thumb.1000_0.jpeg",),),),),// 表單Form(key: _formKey,child: Column(children: <Widget>[// 昵稱TextFormField(decoration: InputDecoration(labelText: "昵稱",hintText: "請輸入登錄昵稱",helperText: '',),onSaved: (String value) {_nickname = value;},autovalidate: _autoValidate,validator: _validateNickname,),// 密碼TextFormField(decoration: InputDecoration(labelText: "密碼",hintText: "請輸入登錄密碼",helperText: '',),obscureText: true,onSaved: (String value) {_password = value;},autovalidate: _autoValidate,validator: _validatePassword,),// 登錄按鈕Container(child: RaisedButton(child: Text("登錄"),onPressed: _submitForm,color: Theme.of(context).accentColor,elevation: 0.0,),),],),),// 注冊新賬號Container(child: FlatButton(onPressed: () {Navigator.of(context).push(MaterialPageRoute(builder: (context) => SignUp()));},child: Text("注冊新賬號"),),),],),),),);} }

Flutter SliverAppBar 隱藏/顯示導(dǎo)航欄

class MyWidget extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(body: NestedScrollView(headerSliverBuilder: _sliverBuilder,body: Center(child: Text('hahaha'),)),);} }List<Widget> _sliverBuilder(BuildContext context, bool innerBoxIsScrolled) {return <Widget>[SliverAppBar(centerTitle: true, //標(biāo)題居中expandedHeight: 200.0, //展開高度200backgroundColor: Colors.tealAccent,floating: false, //不隨著滑動(dòng)隱藏標(biāo)題pinned: false, //不固定在頂部flexibleSpace: FlexibleSpaceBar(centerTitle: true,background: Image.asset("assets/pic.jpg", fit: BoxFit.cover,),),)]; }

骨架屏

https://pub.dev/packages/pk_skeleton

flutter 全屏背景圖(包括appbar和狀態(tài)欄)

class _HomeState extends State<Home> {@overrideWidget build(BuildContext context) {SelfAdapt _adapt = SelfAdapt.init(context);return Container(width: _adapt.width,height: _adapt.height,decoration: BoxDecoration(image: DecorationImage(image: NetworkImage('https://img.zcool.cn/community/0372d195ac1cd55a8012062e3b16810.jpg'),fit: BoxFit.cover,),),child: Scaffold(backgroundColor: Colors.transparent,appBar: AppBar(elevation: 0,backgroundColor: Colors.transparent,// title: Text('首頁'),),drawer: MyDrawer(),body: Container(width: _adapt.width,padding: _adapt.setfromLTRB(100, 0, 100, 0),child: Text('hello'),),),);} }

極光推送:

注冊賬戶-----創(chuàng)建應(yīng)用-----appkey—應(yīng)用報(bào)名要一致

集成

1.下載依賴

  • Android: 在 /android/app/build.gradle 中添加下列代碼:android: {....defaultConfig {applicationId "替換成自己應(yīng)用 ID"...ndk {//選擇要添加的對應(yīng) cpu 類型的 .so 庫。abiFilters 'armeabi', 'armeabi-v7a', 'x86', 'x86_64', 'mips', 'mips64', 'arm64-v8a', }manifestPlaceholders = [JPUSH_PKGNAME : applicationId,JPUSH_APPKEY : "appkey", // NOTE: JPush 上注冊的包名對應(yīng)的 Appkey.JPUSH_CHANNEL : "developer-default", //暫時(shí)填寫默認(rèn)值即可.]} } void initState() {super.initState();this.initJpush();// ///創(chuàng)建 JPush// JPush jpush = new JPush();// ///配置應(yīng)用 Key// jpush.setup(// appKey: "3cc9670e26b5b3e83cabe979",// channel: "theChannel",// production: false,// /// 設(shè)置是否打印 debug 日志// debug: true,// );}

    或者init:

    void initState() {// TODO: implement initStatesuper.initState();this.initJpush();}//監(jiān)聽極光推送 (自定義的方法)//https://github.com/jpush/jpush-flutter-plugin/blob/master/documents/APIs.mdinitJpush() async {JPush jpush = new JPush();//獲取注冊的idjpush.getRegistrationID().then((rid) {print("獲取注冊的id:$rid");});//初始化jpush.setup(appKey: "17d78ecf32c322db169a1d98",channel: "theChannel",production: false,debug: true, // 設(shè)置是否打印 debug 日志);//設(shè)置別名 實(shí)現(xiàn)指定用戶推送jpush.setAlias("jg123").then((map) {print("設(shè)置別名成功");});try {//監(jiān)聽消息通知jpush.addEventHandler(// 接收通知回調(diào)方法。onReceiveNotification: (Map<String, dynamic> message) async {print("flutter onReceiveNotification: $message");},// 點(diǎn)擊通知回調(diào)方法。onOpenNotification: (Map<String, dynamic> message) async {print("flutter onOpenNotification: $message");},// 接收自定義消息回調(diào)方法。onReceiveMessage: (Map<String, dynamic> message) async {print("flutter onReceiveMessage: $message");},);} catch (e) {print('極光sdk配置異常');}}

    指定設(shè)備推送

    sockio

    var http=require('http');var fs=require('fs'); /*fs內(nèi)置的模塊*/var app=http.createServer(function(req,res){//加載靜態(tài)頁面fs.readFile('app.html',function(err,data){res.writeHead(200,{"Content-Type":"text/html;charset='utf-8'"});res.end(data);}) })//引入socket.io var io = require('socket.io')(app);io.on('connection', function (socket) {console.log('服務(wù)器建立連接了');//服務(wù)器獲取客戶端廣播的數(shù)據(jù)socket.on('addcart',function(data){console.log(data);//服務(wù)器給客戶端發(fā)送數(shù)據(jù)//socket.emit(); /*誰給我發(fā)信息我把信息廣播給誰*///io.emit() ; /*群發(fā) 給所有連接服務(wù)器的客戶都廣播數(shù)據(jù)*///socket.emit('to-client','我是服務(wù)器的數(shù)據(jù)'+data.client);io.emit('to-client','我是服務(wù)器的數(shù)據(jù)'+data.client)})});app.listen(3000);/*使用socket.io 1.安裝npm install socket.io2、引入建立連接var io = require('socket.io')(app);io.on('connection', function (socket) {console.log('服務(wù)器建立連接了');});3、在客戶端 html里面引入jshttp://localhost:3000/socket.io/socket.io.js* */

    指紋

    android:theme="@style/Theme.AppCompat"

    package com.xinxing.luckly_flutter//import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterFragmentActivity class MainActivity: FlutterFragmentActivity() { }

    FlutterIos中使用生物識別認(rèn)證的一些配置
    Info.plist中加入下面配置

    NSFaceIDUsageDescriptionfaceid進(jìn)行身份驗(yàn)證?

    總結(jié)

    以上是生活随笔為你收集整理的Flutter基础笔记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。