还有就是上面涉及到的导包操做,格式以下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]}"),//支持字符串中直接拼接
),
);
});
}
}
复制代码