還有就是上面涉及到的導包操做,格式以下html
//import '路徑'
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
複製代碼
這裏用到的Widget爲StatefulWidget,導入上述文件中任何一個均可以,根據不一樣平臺進行選擇。這三個文件包含內容的大小從上到下依次減小,前兩個文件包含了第三個文件。第一個主要爲Material Design風格UI等(Android平臺主流風格),第二個主要包含Cupertino風格UI等(IOS平臺主流風格),第三個即經常使用Widget等。git
import 'package:flutter/material.dart';
//import 'package:flutter/widgets.dart';
//import 'package:flutter/cupertino.dart';
//規範:同Java,駝峯命名不推薦下劃線等特殊符號
class WhatsappHome extends StatefulWidget {
@override
State createState() {
return null;
}
}
複製代碼
接下來爲createState()方法建立一個返回值,這種狀況能夠考慮直接new一個State,可是會報錯由於State是一個抽象類,因此這裏咱們自定義一個類,繼承State。代碼以下github
class _WhatsAppHomeState extends State{
@override
Widget build(BuildContext context) {
return null;
}
}
複製代碼
上面代碼依舊在同一個文件中,必須實現build方法,同時該方法要求返回一個Widget,前面提到的任何Widget均可以在這裏做爲返回值,但這裏咱們選擇使用Scaffold做爲返回值,Scaffold是一個組合Widget,由Flutter內部幫咱們將多個Widget組合到一塊兒,實現了基本的Material Design佈局結構(Android),經過閱讀源碼能夠查看該Widget由那些Widget組合而來,請自行查閱,Scaffold是及其經常使用的,其子Widget中又包含了其餘組合Widget好比AppBar等,瞭解各個Widget有助於之後自定義(組合)佈局結構。算法
接下來往Scaffold中填充元素,根據上面的效果圖能夠分析出來當前頁面須要哪些Widget,填充後完整代碼以下api
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text("WhatApp"),
elevation: 0.7,//陰影
bottom: new TabBar(//注意這裏bottom是指Appbar的bottom
//硬性規定:TabBar必須配合TabController一塊兒使用(或者TabController的實現類),用於控制tab切換,不然程序報錯
controller: new TabController(length: 4, vsync: this),
indicatorColor: Colors.white,
//選中時顏色
tabs: <Widget>[
new Tab(icon: new Icon(Icons.camera_alt)),
new Tab(text: "CHATS"),
new Tab(text: "STATUS"),
new Tab(text: "CALLS")
],
),
//右側按鈕
actions: <Widget>[
new Icon(Icons.search),
new Padding(padding: const EdgeInsets.symmetric(horizontal: 5.0)),
//常量建議加上const關鍵字(非強制),EdgeInsets表示邊緣插入,symmetric爲對稱插入方向的描述之一
new Icon(Icons.more_vert)
],
),
);
}
}
複製代碼
可是若是使用hot load功能程序會出現以下錯誤後來發現圖片丟了......數組
看到關鍵點便可,能夠看到錯誤中提到了一個叫SingleTickerProviderStateMixin
的類,並指明瞭_WhatsAppHomeState
須要使用它,代碼中有註釋明確表示須要配合TickerProvider來使用Tabbar,而錯誤中提到的類也包含這個單詞,到這裏大體就能夠推斷出來SingleTickerProviderStateMixin
是TickerProvider
的一個子類。那替換成它要求的類便可。網絡
在動手前先看下SingleTickerProviderStateMixin
的具體實現,這裏算是難點了 點擊controller
屬性進入源碼中查看,能夠看到源碼中要求使用TabController
,而這裏直接new一個對象的話會出現以下錯誤app
The argument type 'SingleTickerProviderStateMixin<StatefulWidget>' can't be assigned to the parameter type 'TabController'
複製代碼
大意爲SingleTickerProviderStateMixin
不能轉換爲TabController
,點開SingleTickerProviderStateMixin
源碼以下less
@optionalTypeArgs
mixin SingleTickerProviderStateMixin<T extends StatefulWidget> on State<T> implements TickerProvider {
Ticker _ticker;
...
複製代碼
多的就不看了,第一行就夠,很明顯該類實現了TickerProvider
,在Dart語法中可使用implements
實現其餘類不必定是抽象類,以下異步
abstract class A{
}
abstract class B{
}
class D{
}
abstract class C extends A implements B,D{
}
複製代碼
由於這裏是實現關係,並不是繼承因此對象不能轉換。其餘該類的聲明過程當中還出現了幾個關鍵詞mixin
、on
,一般和前兩個關鍵詞配合使用的還有一個with
,受限於篇幅這裏就不詳述了,看下以下代碼應該就差很少明白了,說明在註釋中 class A { void a() { print("A"); } }
class B {
void b() {
print("B");
}
}
class D {
void b() {
print("D");
}
}
mixin E on D {}//該行表示類E容許被with多繼承,可是受限於類D,再直白點的說法就是:再直白點的說法就是:某X使用with多繼承E,則X必須是繼承D
//with用於實現多繼承,如有同名方法優先級從右往左依次從高到低
class C extends A with B, D, E {
void text() {
this.a();
this.b(); //能夠調用到BD中方法,且輸出D,由於D中b()覆蓋了B中b().
}
}
//如下下寫法將出錯
//class F with E{}由於F和D並沒有繼承關係
複製代碼
這裏須要着重理解一下從事移動開發的對多繼承的概念可能比較薄弱,其次Dart低(高)版本中該關鍵字使用方法可能有差別。
確保上面的三個關鍵字理解了,再回頭看SingleTickerProviderStateMixin
得使用就比較明瞭了。完整代碼以下
import 'package:flutter/material.dart';
import 'package:flutter/src/foundation/diagnostics.dart';
import 'package:flutter_teaching/whatsapp/pages/call_screen.dart';
import 'package:flutter_teaching/whatsapp/pages/camera_screen.dart';
import 'package:flutter_teaching/whatsapp/pages/chat_screen.dart';
import 'package:flutter_teaching/whatsapp/pages/status_screen.dart';
//import 'package:flutter/widgets.dart';
//import 'package:flutter/cupertino.dart';
//規範:同Java,駝峯命名不推薦下劃線等特殊符號
class WhatsappHome extends StatefulWidget {
@override
State createState() {
return new _WhatsAppHomeState();
}
}
class _WhatsAppHomeState extends State with SingleTickerProviderStateMixin {
TabController _tabController;
@override
void initState() {
super.initState();
_tabController = new TabController(length: 4, vsync: this, initialIndex: 1);
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text("WhatApp"),
elevation: 0.7, //陰影
bottom: new TabBar(
controller: _tabController,
//TabBar必須配合TabController一塊兒使用
indicatorColor: Colors.white,
//選中時顏色
tabs: <Widget>[
new Tab(icon: new Icon(Icons.camera_alt)),
new Tab(text: "CHATS"),
new Tab(text: "STATUS"),
new Tab(text: "CALLS")
],
),
actions: <Widget>[
new Icon(Icons.search),
new Padding(padding: const EdgeInsets.symmetric(horizontal: 5.0)),
//常量建議加上const關鍵字(非強制),EdgeInsets表示邊緣插入,symmetric爲對稱插入方向的描述之一
new Icon(Icons.more_vert)
],
),
//TabBarView表示爲TabBar的頁面,該空間會自動按順序關聯TabBar,這裏簡單建立了4個類
// 每一個類寫法徹底相同以下,記得導入文件
//class CallsScreen extends StatelessWidget {
// @override
// Widget build(BuildContext context) {
// return new Center(
// child: new Text(
// "Calls",
// style: new TextStyle(fontSize: 20.0),
// ),
// );
// }
//}
//注意這是Scaffold中屬性
body: new TabBarView(
controller: _tabController,
children: <Widget>[
new CameraScreen(),
new ChatScreen(),
new StatusScreen(),
new CallsScreen(),
],
),
floatingActionButton: new FloatingActionButton(
backgroundColor: Theme.of(context).accentColor,
child: new Icon(
Icons.message,
color: Colors.white,
),
onPressed: () => print("open chat")),
);
}
}
複製代碼
這裏說明一下Dart的語法是嵌套+構造的寫法,若是遇到不清楚的Widget不知道有些啥屬性那麼點進去看一下它的構造方法就明白了。這裏代碼可能看起來不清晰,但在AS中會自動填充標記所嵌套的層次。 到目前爲止整個功能剩餘對攝像頭的支持了,目前效果圖以下
然而並無圖
接下來對camera_screen.dart
文件改造下。Flutter中攝像頭使用的官方連接,大體看一下知道大概哪些步驟。 前面爲了方便對四個View都是使用的StatelessWidget
,也就是說類容固定,而要使用攝像頭必須使用StatefulWidget
,還須要添加依賴。其次這裏還涉及到異步,文檔第二步中的使用到的await
,async
,Future
就是Flutter中異步操做須要用到的關鍵詞,經常使用於網絡請求,以及耗時任務等阻塞線程的操做中。以後初始化CameraController
這玩意就是Android中的CameraManager
,不初始化沒法使用拍照等功能。最後建立一個Widget用於顯示攝像頭獲取到的畫面,這裏指定使用CameraPreview
來展現畫面。剩餘的兩部分別是拍照和展現照片。
第一步:按照文檔中的第一步,添加依賴。添加後pubspec.yaml
文件以下,添加以後點擊左上角的Packages get
同步完成後進行下一步
# 注意這裏的依賴縮進須要注意 縮進錯誤會致使找不到包報錯
dependencies:
flutter:
sdk: flutter
camera:
path_provider:
path:
複製代碼
第二步:獲取可用Camera列表,並從列表中獲取第一個可用的攝像頭代碼以下
// Obtain a list of the available cameras on the device.
final cameras = await availableCameras();
// Get a specific camera from the list of available cameras.
final firstCamera = cameras.first;
複製代碼
重點來了,前面講到了mixin
、with
、on
三個關鍵字以及組合使用,這裏依舊是三個相互關聯的關鍵字分別是await
、Future
、async
await
用在調用的異步方法時做爲前置修飾。若某方法體內使用await
關鍵字,則該方法一定須要使用async
修飾,若由await
修飾的部分爲返回值,則返回值類型必須聲明爲Future
類型
Future getCamera() async {
// 該方法調用異步方法availableCameras(),須要使用await來調用,且該方法須要使用async修飾,返回值爲Future
final cameras = await availableCameras();
// Get a specific camera from the list of available cameras.
final firstCamera = cameras.first;
}
複製代碼
上面代碼中異步方法availableCameras()來自文件'package:camera/camera.dart';,
第三步:初始化CameraController
class TakePictureScreen extends StatefulWidget {
final CameraDescription camera;
const TakePictureScreen({
Key key,
@required this.camera,
}) : super(key: key);
@override
TakePictureScreenState createState() => TakePictureScreenState();
}
class TakePictureScreenState extends State<TakePictureScreen> {
// Add two variables to the state class to store the CameraController and
// the Future.
CameraController _controller;
Future<void> _initializeControllerFuture;
@override
void initState() {
super.initState();
// To display the current output from the camera,
// create a CameraController.
_controller = CameraController(
// Get a specific camera from the list of available cameras.
widget.camera,
// Define the resolution to use.
ResolutionPreset.medium,
);
// Next, initialize the controller. This returns a Future.
_initializeControllerFuture = _controller.initialize();
}
@override
void dispose() {
// Dispose of the controller when the widget is disposed.
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
// Fill this out in the next steps.第四步中完成該方法
}
}
複製代碼
第四步:使用CameraPreview顯示攝像頭獲取到的畫面
//第三部中的build方法
@override
Widget build(BuildContext context) {
if (!controller.value.isInitialized) {
return new Container();
}
return new AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: new CameraPreview(controller),
);
}
複製代碼
最後,在main函數中異步獲取可用camera,並將該對象以構造的方式傳入到camera_screen()中。繼續按照官方文檔中的第五六步能夠實現拍照且,將照片顯示出來。
注意:前面講過Flutter類名和文件名無關,且能夠有多個一級類好比前面講到的WhatsappHome中的_WhatsAppHomeState,那麼要在_WhatsAppHomeState中調用到WhatsappHome中的變量可使用以下寫法
class _WhatsAppHomeState extends State<WhatsappHome> with SingleTickerProviderStateMixin {
複製代碼
經過查看State類的源碼可得知該類支持泛型,而State一般是做爲StatefulWidget中createState的返回值出現,故State中泛型一般傳入與之關聯的StatefulWidget。
@optionalTypeArgs
abstract class State<T extends StatefulWidget> extends Diagnosticable {...}
複製代碼
最後是Flutter中ListView的使用 在Flutter中滑動控件也叫ListView,其相關的列表控件包括ScrollView以及直接或間接繼承自該類的子控件如CustomScrollView、GridView等 ListView官方文檔 經過閱讀文檔或者查看源碼能夠發現,ListView共四種用法,原文以下
/// 1. The default constructor takes an explicit [List<Widget>] of children. This
/// constructor is appropriate for list views with a small number of
/// children because constructing the [List] requires doing work for every
/// child that could possibly be displayed in the list view instead of just
/// those children that are actually visible.默認構造方法,WIdget數組便可,適用數少許,固定的列表
///
/// 2. The [ListView.builder] constructor takes an [IndexedWidgetBuilder], which
/// builds the children on demand. This constructor is appropriate for list views
/// with a large (or infinite) number of children because the builder is called
/// only for those children that are actually visible.根據須要構建子項。此構造函數適用於具備大量(或無限)子項數的列表視圖
///
/// 3. The [ListView.separated] constructor takes two [IndexedWidgetBuilder]s:
/// `itemBuilder` builds child items on demand, and `separatorBuilder`
/// similarly builds separator children which appear in between the child items.
/// This constructor is appropriate for list views with a fixed number of children.
/// 按需構建子項,而separatorBuilder相似地構建出如今子項之間的子項(分割線或者多層次的ListView)。此構造函數適用於具備固定數量子項的列表視圖。
///
/// 4. The [ListView.custom] constructor takes a [SliverChildDelegate], which provides
/// the ability to customize additional aspects of the child model. For example,
/// a [SliverChildDelegate] can control the algorithm used to estimate the
/// size of children that are not actually visible.
/// 採用SliverChildDelegate,它提供了自定義子模型的其餘方面的功能。例如,SliverChildDelegate能夠控制用於估計實際上不可見的子項大小的算法。
/// 這個涉及到Sliver以及其衍生子類,動畫效果都很好,漸變漸隱之類的如SliverAppBar等。
複製代碼
下面是改造後的ChatScreen,其中方向由構造方法中的參數決定,默認豎向Axis scrollDirection = Axis.vertical,
class ChatScreen extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return new ChatScreenState();
}
}
class ChatScreenState extends State<ChatScreen> {
final List<String> entries = <String>["A", "B", "C", "A", "B", "C"];
final List<int> background = <int>[50, 100, 200, 300, 400, 500];//顏色深度有一張表,在一個叫Gallery的項目中找到的
@override
Widget build(BuildContext context) {
//不一樣構造方法
// ListView.separated(itemBuilder: null, separatorBuilder: null, itemCount: null);
// ListView.builder(itemBuilder: null);
// ListView.custom(childrenDelegate: null);
return new ListView.separated(
padding: const EdgeInsets.all(5.0),
itemCount: entries.length,
separatorBuilder: (BuildContext context, int index) => const Divider(),//默認分隔線
itemBuilder: (BuildContext context, int index) {
return Container(
height: 100,
color: Colors.red[background[index]],
child: new Center(
child: new Text("Item_${entries[index]}"),//支持字符串中直接拼接
),
);
});
}
}
複製代碼