標題後面的時間是我用來記錄自己更新上去的時間,不用在意
設定元件為中文(2020.05.20)
剛開始去使用一些彈窗元件(例如時間選擇器什麼的),發現裡面的確定和取消都是英文,就去百度了一下方法。
改配置檔案
在pubspec.yaml檔案的對應位置加上
flutter_localizations:
sdk: flutter
複製程式碼
修改主入口
在主入口函式新增(這裡還需要引入package:flutter_localizations/flutter_localizations.dart)
import 'package:flutter_localizations/flutter_localizations.dart';
複製程式碼
//設定元件為中文
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
//此處
const Locale('zh', 'CH'),
const Locale('en', 'US'),
],
locale: Locale('zh'),
複製程式碼
圖片上傳(2020.05.20)
這個坑,花了我整整一天的時間(其實就是電腦卡,10分鐘一小卡,30分鐘一大卡,不好除錯)。
外掛
開始
直接貼程式碼
import 'dart:collection';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:image_picker/image_picker.dart';
import 'package:flutter/material.dart';
import '../../base/Http.dart';//這個是自己封裝後的http
import 'package:http_parser/http_parser.dart';
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
File _image;
//照相
Future getImage() async {
var image = await ImagePicker.pickImage(source: ImageSource.camera);
setState(() {
uploadImgFunc(image);
_image = image;
});
}
//從相機選擇
Future openGallery() async {
var image = await ImagePicker.pickImage(source: ImageSource.gallery);
setState(() {
uploadImgFunc(image);
_image = image;
});
}
uploadImgFunc(File image) async {
String path = image.path;//檔案路徑
String name = path.substring(path.lastIndexOf("/") + 1, path.length);//檔名
String suffix = name.substring(name.lastIndexOf(".") + 1, name.length);//檔案字尾
FormData formData = FormData.fromMap({
"uploadFile":await MultipartFile.fromFile(
path,
filename: name,
contentType:MediaType('image',suffix)//contentType這個引數看情況,下面會講到
),
});
Dio dio = new Dio();
var result = await dio.post<String>("介面", data: formData);
print(result);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Image Picker Example'),
),
body: Center(
child: _image == null ? Text('No image selected.') : Image.file(_image),
),
floatingActionButton: FloatingActionButton(
onPressed: getImage,
tooltip: 'Pick Image',
child: Icon(Icons.add_a_photo),
),
);
}
}
複製程式碼
上面就是一個例子了,現在開始講解。
首先,我們需要引入image_picker這個包,在pubspec.yaml檔案裡面新增對應項
然後在對應檔案引入import 'dart:io';//io操作
import 'package:dio/dio.dart';
import 'package:image_picker/image_picker.dart';
複製程式碼
寫呼叫方法(這裡以拍照為例)
Future getImage() async {
var image = await ImagePicker.pickImage(source: ImageSource.camera);//這裡用到了對應外掛
setState(() {
uploadImgFunc(image);
_image = image;//_image需要在外層定義
});
}
複製程式碼
寫上傳方法(重點)
uploadImgFunc(File image) async {
String path = image.path;
String name = path.substring(path.lastIndexOf("/") + 1, path.length);
String suffix = name.substring(name.lastIndexOf(".") + 1, name.length);
FormData formData = FormData.fromMap({
"uploadFile":await MultipartFile.fromFile(//這裡記得加await
path,
filename: name,
contentType:MediaType('image',suffix)
),
});
Dio dio = new Dio();
var result = await dio.post<String>('介面', data: formData);
print(result);
}
複製程式碼
dio3.0之後就使用MultipartFile不再使用UploadFileInfo。
MediaType這個需要額外引一個標頭檔案,不然會報錯
import 'package:http_parser/http_parser.dart';
複製程式碼
假如,你們的後端是可以用二進位制的方法來上傳的話,
contentType:MediaType('image',suffix)
複製程式碼
上面那一行可以去掉,不然的話一定要加上,不然後端如果是判斷檔案型別的話可能就過不了。我就是因為這個卡了很久......
ios真機除錯的時候用相機(我同事說的)好像會閃退,因為我沒有mac系統,也沒錢,這裡找個了文章。大家看看吧。
官網文件好像也有說,之前沒看仔細。需要在配置檔案裡面加東西
獲取元素位置(2020.05.20)
現在,我要獲取某個元素的位置,然後按照位置來定義一個彈窗(showMenu)的位置,例如下圖
定義一個key
GlobalKey positionKey = GlobalKey();
複製程式碼
在需要定位的元素上使用
Text(
'其他',
key: positionKey,
textAlign: TextAlign.center,
style: TextStyle(
height: 2.5,
color: Color.fromRGBO(255, 106, 0, 1),
fontSize: ScreenAdaper.size(30),
)
複製程式碼
使用key來獲取位置
在函式內去獲取
RenderBox renderBox =
positionKey.currentContext.findRenderObject();
var offset = renderBox.localToGlobal(Offset.zero);
print(offset.dx)//橫座標
print(offset.dx)//縱座標
複製程式碼
ios數字軟鍵盤沒有'完成'按鈕(2020.05.20)
當我們把輸入框(TextField)的型別(keyboardType)設定number時,Android和ios就會彈出數字鍵盤而不是英文鍵盤,但是ios數字軟鍵盤是沒有所謂的完成按鈕(就是點選後軟鍵盤會收回去)。這時候,我們就需要用到一個外掛了
keyboard_actions
這是一個可以解決上面問題的外掛,如何引入同上面提過的。
使用
在需要用到的元件內先定義一個基礎設定,這裡的actions裡面KeyboardAction有很多設定,推薦去看一下官方文件
KeyboardActionsConfig _buildConfig(BuildContext context) {
return KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.ALL,
keyboardBarColor: Colors.grey[200],
nextFocus: true,
actions: [
KeyboardAction(
focusNode: _nodeNumberNode,//下面會提到
toolbarButtons: [
(node) {
return GestureDetector(
onTap: () => node.unfocus(),
child: Container(
padding: EdgeInsets.fromLTRB(8, 8, 16, 8),
child: Text(
"完成",
style: TextStyle(color: Colors.black),
),
),
);
},
],
),
],
);
}
複製程式碼
然後,我們需要定義一個FocusNode來對應設定和輸入框
final FocusNode _nodeNumberNode = FocusNode();
複製程式碼
輸入框設定,需要包一層KeyboardActions
KeyboardActions(
config: _buildConfig(context),//上面定義的那個基礎設定
chlid:Container(
child:TextField(
focusNode: _nodeNumberNode,
keyboardType: TextInputType.number,
textInputAction: TextInputAction.done,
decoration: InputDecoration(
hintText: '請輸入',
border: InputBorder.none,
),
autofocus: false,
onChanged: (value) {
setState(() {
//賦值操作
});
},
)
)
)
複製程式碼
這樣就可以了,不過有個要注意的點,然後你的輸入框是放在ListView裡面的話,需要給ListView加一個屬性
shrinkWrap: false,
複製程式碼
showDialog檢視資料重新整理不及時(2020.05.21)
使用showDialog後,通過setState()無法更新當前dialog。因為dialog其實是另一個頁面,準確地來說是另一個路由,因為dialog的關閉也是通過navigator來pop的,所以它的地位跟你當前主頁面一樣,所以我們需要使用另外的方式來實現。
我們先看看showDialog的最基本寫法
showDialog(
context: context,
builder: (context) {
return AlertDialog(
...
);
}
);
複製程式碼
第一種方法
解決問題後的寫法
showDialog(
context: context,
builder: (context) {
return StatefulBuilder(//這裡需要先使用StatefulBuilder
builder: (context, state) {//多了個state
return AlertDialog(
title: Text('彈窗標題'),
content: Container(
height: ScreenAdaper.height(200),//ScreenAdaper這個是自己的螢幕適配,大家可以直接使用數字的
child: Column(
children: <Widget>[
InkWell(
child: Row(
children: <Widget>[
Image.asset(
'images/task_leadicon.png',
width:ScreenAdaper.height(60),
)
],
),
onTap: () async {
state(() {//這個是重點
//...這裡進行賦值操作
});
},
),
],
),
),
actions: <Widget>[
FlatButton(
child: Text('確認'),
onPressed: () async {
Navigator.pop(context);
},
),
FlatButton(
child: Text('取消'),
onPressed: () {
Navigator.pop(context);
},
),
],
);
},
);
});
複製程式碼
第二種方法
這個還有另外一種寫法,就是自己再去定義一個彈窗,不過這種我沒有試過,這裡直接貼別人的程式碼
showDialog(
context: context,
builder: (context) {
String label = 'test';
return DialogContent(
label: label,
);
});
class DialogContent extends StatefulWidget {
final String label;
DialogContent({Key key, this.label}) : super(key: key);
@override
State<StatefulWidget> createState() => DialogContentState();
}
class DialogContentState extends State<DialogContent> {
String label = '';
@override
void initState() {
super.initState();
label = widget.label;
}
@override
Widget build(BuildContext context) {
return GestureDetector(
child: Text(label),
onTap: () {
setState(() {
label = 'test9';
});
},
);
}
}
複製程式碼
上面的坑都是我自己在開發遇到並且解決了的,如果你也遇到這些坑,而且我提的方法也有用的話,給個贊吧!!!如果沒有起到作用,能否在評論區留下有用的方法