bool _visiable = false;
Opacity(
opacity: _visiable ? 1.0 : 0.0,
child: const Text('Now you see me, now you don\'t!'), ) 复制代码
隐藏后仍然进行渲染,仍然占有空间,仍然能够进行交互前端
Offstage(
offstage: true,
child: child
)
);
复制代码
隐藏后不会进行渲染,不占用空间android
Visibility(
visible: false,
child: child
)
)
复制代码
隐藏后不会进行渲染,不占用空间ios
Swiper(
itemCount: w_list.length,
//itemWidth: MediaQuery.of(context).size.width,
itemBuilder: _swiperBuilder,
pagination: SwiperPagination(
alignment: Alignment.bottomCenter,
margin: const EdgeInsets.fromLTRB(0, 0, 0, 10),
builder: DotSwiperPaginationBuilder(
color: Colors.black54,
activeColor: Colors.blue,
)
),
controller: SwiperController(),
autoplayDisableOnInteraction: true,
scrollDirection: Axis.horizontal,
autoplay: false,
index: initial_index,//设置成开始浏览时点击的item的index值
viewportFraction: 1.0,
scale: 1.0,
//layout: SwiperLayout.STACK,
//onTap: (index) => print('点击了第$index'),
),
复制代码
使用pageview能够实现放大左右切换,可是相比swiper
(1)没有自带分页器
(2)pageview的pageViewController.jumpToPage()方法在必须在PageView构建后才可使用,可是咱们但愿开始浏览时就已经跳转到目标item,没法知足需求。Swiper只须要简单设置index即可以解决上述问题git
在Swiper的itemBuilder中,对于视频类型的item,返回的widget并不是视频,而是视频的第一帧加一个播放图标堆叠在上面,widget代码以下github
Widget get_video_widget(String url)
{
/*
返回存放视频的容器,参数是视频第一帧url
*/
return Container(
decoration: BoxDecoration(
image: new DecorationImage(
// fit:BoxFit.fill,
image: new NetworkImage(url, scale: 1.2)),
),
child:Opacity(
opacity: 0.8,
child: Center(
child:Icon(Icons.play_circle_filled,color: Colors.white,)
),
),
);
}
复制代码
当检测到用户点击时,弹出dialog形式的新的界面进行视频播放,而非Swiper内部播放,(Swiper内部播放会出现不少问题)json
IjkMediaController jk_controller = IjkMediaController();
return GestureDetector(
child:w_list[index],//是上面 get_video_widget返回的widget
onTap: (){
showIJKDialog();
},
);
}
@override
void dispose() {
jk_controller?.dispose();
super.dispose();
}
void showIJKDialog() async {
await jk_controller.setDataSource(
DataSource.network(
"http://img.ksbbs.com/asset/Mon_1703/05cacb4e02f9d9e.mp4"),
);
await jk_controller.play();
await showDialog(
context: context,
builder: (_) => _buildIJKPlayer(),
);
jk_controller.pause();
}
_buildIJKPlayer() {
return IjkPlayer(
mediaController: jk_controller,
);
}
复制代码
播放效果以下 bash
这里使用的用来播放视频的flutter库叫作flutter_ijkplayer,
相比较video_player,拥有更好的ui界面及更多的视频操做功能,
相比较cheiwe,拥有更快的加载速度,更高的稳定性
一个很好的flutter_ijkplayer demo地址服务器
String url = "http://xxxxxxx"
Map<String,String> json_data = {};
json_data.addAll({"xx":xxx});
await http.post(url, body: json_data)
.then((response) {
print("post方式->status: ${response.statusCode}");
print("post方式->body: ${response.body}");
});
复制代码
ClipOval(
child: Container(
height: MediaQuery.of(context).size.height/4,
width: MediaQuery.of(context).size.height/4,
child: Image.file(File(widget.imagePath),fit: BoxFit.fill,),
),
);
复制代码
import 'dart:async';
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:path/path.dart' show join;
import 'package:path_provider/path_provider.dart';
Future<void> main() async {
// 获取设备可用摄像头
final cameras = await availableCameras();
// 取第一个摄像头
final firstCamera = cameras.first;
runApp(
MaterialApp(
theme: ThemeData.dark(),
home: TakePictureScreen(
// Pass the appropriate camera to the TakePictureScreen widget.
camera: firstCamera,
),
),
);
}
// A screen that allows users to take a picture using a given //camera.
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> {
CameraController _controller;
Future<void> _initializeControllerFuture;
@override
void initState() {
super.initState();
// 获取摄像头的控制器
_controller = CameraController(
// Get a specific camera from the list of available cameras.
widget.camera,
ResolutionPreset.medium,
);
// 初始化控制器
_initializeControllerFuture = _controller.initialize();
}
@override
void dispose() {
//销毁控制器
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Take a picture')),
//FutureBuilder来确保控制器已经被初始化
body: FutureBuilder<void>(
future: _initializeControllerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
//肯定初始化完成,打开相机
return CameraPreview(_controller);
} else {
// 不然,展现进度条.
return Center(child: CircularProgressIndicator());
}
},
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.camera_alt),
onPressed: () async {
try {
// 确保相机已经被初始化
await _initializeControllerFuture;
final path = join(
//获得图片保存路径
(await getTemporaryDirectory()).path,
'${DateTime.now()}.png',
);
// 进行拍照
await _controller.takePicture(path);
// 拍照后进行展现
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DisplayPictureScreen(imagePath: path),
),
);
} catch (e) {
print(e);
}
},
),
);
}
}
//展现拍照结果的界面
class DisplayPictureScreen extends StatelessWidget {
final String imagePath;
const DisplayPictureScreen({Key key, this.imagePath}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Display the Picture')),
//根据路径获得图片
body: Image.file(File(imagePath)),
);
}
}
复制代码
使用Container的decoration设置背景,
内嵌---Container,经过decoration的gradient设置背景图上层的一个渐变色渲染
内嵌---Stack,进行布局cookie
一些color使用Color.fromRGBO(247, 64, 106, 0.5),经过调节透明度实现一个悬浮效果网络
new Container(
decoration: new BoxDecoration(
image: backgroundImage,
),
child: new Container(
decoration: new BoxDecoration(
gradient: new LinearGradient(
colors: <Color>[
const Color.fromRGBO(162, 146, 199, 0.8),
const Color.fromRGBO(51, 51, 63, 0.9),
],
stops: [0.2, 1.0],
begin: const FractionalOffset(0.0, 0.0),
end: const FractionalOffset(0.0, 1.0),
)),
child: new ListView(
padding: const EdgeInsets.all(0.0),
children: <Widget>[
new Stack(
alignment: AlignmentDirectional.bottomCenter,
children: <Widget>[
new Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(height: 10,
child:Text("悬浮设计")),
],
),
],
),
],
))),
复制代码
注意:
Center(child: CircularProgressIndicator());
复制代码
代码示例:
FutureBuilder<void>(
future: _initializeControllerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// If the Future is complete, display the preview.
return CameraPreview(_controller);
} else {
// Otherwise, display a loading indicator.
return Center(child: CircularProgressIndicator());
}
},
),
复制代码
shared_preferences: ^0.4.3
复制代码
final prefs = await SharedPreferences.getInstance();
prefs.setString("name","Jerry");
复制代码
3.取数据
final prefs = await SharedPreferences.getInstance();
final name = prefs.getString("count") ?? "";
复制代码
WidgetsBinding widgetsBinding;//用来监听build结束后的响应事件
@override
void initState() {
super.initState();
widgetsBinding = WidgetsBinding.instance;
widgetsBinding.addPostFrameCallback((callBack){
dealWithAutoLand();
dealWithRememberPw();
});
}
复制代码
使用两个文件
InputFields.dart构建单独一个输入框widget,代码
import 'package:flutter/material.dart';
//对应一个输入框
class InputFieldArea extends StatelessWidget {
final String hint;
final bool obscure;
final IconData icon;
TextEditingController controller;
FormFieldValidator validator;
InputFieldArea({this.hint, this.obscure, this.icon,this.controller,this.validator});
@override
Widget build(BuildContext context) {
return (new Container(
decoration: new BoxDecoration(
border: new Border(
bottom: new BorderSide(
width: 0.5,
color: Colors.white24,
),
),
),
child: new TextFormField(
controller: controller,
autofocus: true,
obscureText: obscure,
validator: validator,
style: const TextStyle(
color: Colors.white,
),
decoration: new InputDecoration(
icon: new Icon(
icon,
color: Colors.white,
),
border: InputBorder.none,
hintText: hint,
hintStyle: const TextStyle(color: Colors.white, fontSize: 15.0),
contentPadding: const EdgeInsets.only(
top: 30.0, right: 30.0, bottom: 30.0, left: 5.0),
),
),
));
}
}
复制代码
form.dart,组织全部输入框,代码
TextEditingController studentNumberControllerLogin = new TextEditingController();
TextEditingController passwordNumberControllerLogin = new TextEditingController();
//登录的输入表单,包含两个输入框
class loginFormContainer extends StatelessWidget {
Map<String,String> inputData;
//获得输入数据函数
void getData()
{
inputData = new Map();
inputData["studentNumber"] = studentNumberControllerLogin.text.toString();
inputData["password"] = passwordNumberControllerLogin.text.toString();
}
@override
Widget build(BuildContext context) {
return (new Container(
margin: new EdgeInsets.symmetric(horizontal: 20.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new Form(
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
new InputFieldArea(
controller: studentNumberControllerLogin,
hint: "学号",
obscure: false,
icon: Icons.person_outline,
),
new InputFieldArea(
controller: passwordNumberControllerLogin,
hint: "密码",
obscure: true,
icon: Icons.lock_outline,
),
],
)),
],
),
));
}
}
复制代码
appBar:PreferredSize(
preferredSize: Size.fromHeight(2.0),//修改appbar高度
child: AppBar(
title: Text("地大知识交流平台"),
),
),
复制代码
1.gradient使用LinearGradinet,代码
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.cyan, Colors.blue, Colors.cyan],
),
),
复制代码
flutter_screenutil: ^0.5.3
复制代码
//填入设计稿中设备的屏幕尺寸
ScreenUtil.instance = ScreenUtil(width: 1080, height: 1920)..init(context);
复制代码
ScreenUtil.getInstance().setSp(28)
复制代码
Container(
width: ScreenUtil.getInstance().setWidth(300),
height: ScreenUtil.getInstance().setWidth(300),
),
复制代码
ScreenUtil.getInstance().setWidth(300),
复制代码
ScreenUtil.getInstance().setHeight(300),
复制代码
第一步,安装dio
dio: ^2.1.0
复制代码
第二步,封装dio
import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/dio.dart';
//普通格式的header
Map<String, dynamic> headers = {
"Accept":"application/json",
// "Content-Type":"application/x-www-form-urlencoded",
};
//json格式的header
Map<String, dynamic> headersJson = {
"Accept":"application/json",
"Content-Type":"application/json; charset=UTF-8",
};
class HttpUtil {
Dio dio;
Map<String, dynamic> headers;
BaseOptions options;
HttpUtil({String baseUrl}) {
headers = {
"Accept":"application/json",
// "Content-Type":"application/x-www-form-urlencoded",
};
print('dio赋值');
// 或者经过传递一个 `options`来建立dio实例
options = BaseOptions(
// 请求基地址,通常为域名,能够包含路径
baseUrl: baseUrl,
//链接服务器超时时间,单位是毫秒.
connectTimeout: 10000,
//[若是返回数据是json(content-type),dio默认会自动将数据转为json,无需再手动转](https://github.com/flutterchina/dio/issues/30)
responseType:ResponseType.plain,
/// 响应流上先后两次接受到数据的间隔,单位为毫秒。若是两次间隔超过[receiveTimeout],
/// [Dio] 将会抛出一个[DioErrorType.RECEIVE_TIMEOUT]的异常.
/// 注意: 这并非接收数据的总时限.
receiveTimeout: 3000,
headers: headers,
);
dio = new Dio(options);
dio.interceptors.add(CookieManager(CookieJar()));
}
get(url, {data, options, cancelToken}) async {
print('get请求启动! url:$url ,body: $data');
Response response;
try {
response = await dio.get(
url,
cancelToken: cancelToken,
);
print('get请求成功!response.data:${response.data}');
} on DioError catch (e) {
if (CancelToken.isCancel(e)) {
print('get请求取消! ' + e.message);
}
print('get请求发生错误:$e');
}
return response.data;
}
post(url, {data, options, cancelToken}) async {
print('post请求启动! url:$url ,body: $data');
Response response;
try {
response = await dio.post(
url,
data: data,
);
print('post请求成功!response.data:${response.data}');
} on DioError catch (e) {
if (CancelToken.isCancel(e)) {
print('post请求取消! ' + e.message);
}
print('post请求发生错误:$e');
}
return response.data;
}
}
复制代码
第三步,上传图片
String url = "http://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
FormData formData = new FormData.from({
"image01": new UploadFileInfo(file, "head.jpg")
});
HttpUtil httpUtil = new HttpUtil(baseUrl: url);
String response = await httpUtil.post(url,data: formData);
print(response);
复制代码
第一步,安装toast插件
toast: ^0.1.3
复制代码
第二步,使用
//显示toast
void showToastContent(String content)
{
Toast.show(content,curContext,duration: Toast.LENGTH_LONG,gravity: Toast.BOTTOM);
}
复制代码
设置异常处理块,思想是捕获到异常则表明网络链接失败
List<String> splits = response.split("_");
String resultMark = splits[0];
String content;
try
{
content = splits[1];
}catch(e,s)
{
content = "网络缘由,登录失败";
}
复制代码
DateTime lastTime;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text("点两次返回键退出"),),
body: Builder(builder: (context)=>WillPopScope(
onWillPop: () async {
if (lastTime == null || DateTime.now().difference(lastTime) >
Duration(seconds: 1)) {
lastTime = DateTime.now();
Scaffold.of(context).showSnackBar(SnackBar(content: Text("再点一次退出!")));
return false;
}
return true;
},
child: Container(
alignment: Alignment.center,
child: Text("1秒内连续按两次返回键退出"),
)
))
)
);
}
复制代码
import 'package:flutter/services.dart';
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]).then((_){});
复制代码
main函数内部放
ErrorWidget.builder = (FlutterErrorDetails details) => Container();
复制代码
1 安装插件
flutter_local_notifications: ^0.8.4
复制代码
2 配置android
2.1 AndroidManifest.xml权限添加
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
复制代码
2.2 drawable下建立图标名为app_icon
3 配置ios 这一步不须要作,只要一步一步来,后面的代码中会包含
4 相关代码
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
//初始化
var initializationSettingsAndroid =
AndroidInitializationSettings('app_icon');
var initializationSettingsIOS = IOSInitializationSettings(
onDidReceiveLocalNotification: onDidReceiveLocalNotification);
var initializationSettings = InitializationSettings(
initializationSettingsAndroid, initializationSettingsIOS);
flutterLocalNotificationsPlugin = new FlutterLocalNotificationsPlugin();
flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: onSelectNotification);
//显示通知的函数
Future<void> showNotification(String content) async {
var androidPlatformChannelSpecifics = AndroidNotificationDetails(
'your channel id', 'your channel name', 'your channel description',
importance: Importance.Max, priority: Priority.High, ticker: 'ticker');
var iOSPlatformChannelSpecifics = IOSNotificationDetails();
var platformChannelSpecifics = NotificationDetails(
androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
int notiId = prefs.getInt("notiId") ?? 0;
prefs.setInt("notiId",notiId + 1);
await flutterLocalNotificationsPlugin.show(
notiId, '地大支持平台', "有人解答了你的问题", platformChannelSpecifics,
payload: content);
}
//通知点击事件
Future<void> onSelectNotification(String payload) async {
if (payload != null) {
print("payload*****************************" + payload);
}
await Navigator.push(
projectTopContext,
MaterialPageRoute(builder: (context) => showAnswerComplete(xxx)),
);
}
//适配ios老版本,与点击事件响应一致
Future onDidReceiveLocalNotification(
int id, String title, String body, String payload) async {
// display a dialog with the notification details, tap ok to go to another page
showDialog(
context: context,
builder: (BuildContext context) => new CupertinoAlertDialog(
title: new Text(title),
content: new Text(body),
actions: [
CupertinoDialogAction(
isDefaultAction: true,
child: new Text('Ok'),
onPressed: () async {
Navigator.of(context, rootNavigator: true).pop();
Navigator.push(
projectTopContext,
MaterialPageRoute(builder: (context) => showAnswerComplete(xxx))
);
},
)
],
),
);
}
复制代码