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))
);
},
)
],
),
);
}
複製代碼