/// 自定义的用户协议组件。
class UserAgreement extends StatelessWidget{
@override
Widget build(BuildContext context) {
// 文本(`Text`)组件,是一系列具有单一样式的文本。
// 文本.丰富(`Text.rich`)构造函数,则是使用文字跨度(`TextSpan`)组件创建文本。
return Text.rich(
// 文字跨度(`TextSpan`)组件,不可变的文本范围。
TextSpan(
// 文本(`text`)属性,跨度中包含的文本。
text: '登录即同意',
// 样式(`style`)属性,应用于文本和子组件的样式。
style: _lowProfileStyle,
children: [
TextSpan(
// 识别(`recognizer`)属性,一个手势识别器,它将接收触及此文本范围的事件。
// 手势(`gestures`)库的点击手势识别器(`TapGestureRecognizer`)类,识别点击手势。
recognizer: TapGestureRecognizer()..onTap = () {
print('点击了“服务条款”');
},
text: '“服务条款”',
style: _highProfileStyle,
),
TextSpan(
text: '和',
style: _lowProfileStyle,
),
TextSpan(
recognizer: TapGestureRecognizer()..onTap = () {
print('点击了“隐私政策”');
},
text: '“隐私政策”',
style: _highProfileStyle,
),
],
),
);
}
}
解决Flutter ListView 或者SingleChildScrollView 嵌套 ListView.builder滑动冲突
原因
SingleChildScrollView 和 ListView 都有滚动属性physics 他们默认是都是可以滚动的,
ListView 嵌套 ListView.builder 需要后者shrinkWrap = true,不然报错;
解决方式
禁用 ListView 的滚动physics 保留 SingleChildScrollView 的滚动
Listview 执行 physics 属性 new NeverScrollableScrollPhysics(), //禁用滚动事件
new ListView.builder(
shrinkWrap: true,
physics: new NeverScrollableScrollPhysics(),
)
您可以使用Flutter团队使用相机插件进行颤振.
https://pub.dartlang.org/packages/camera
然后将您的图像和摄像机视图定位在Stack Widget中,如下所示:
return new Stack(
alignment: FractionalOffset.center,
children: <Widget>[
new Positioned.fill(
child: new AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: new CameraPreview(controller)),
),
new Positioned.fill(
child: new Opacity(
opacity: 0.3,
child: new Image.network(
'https://picsum.photos/3000/4000',
fit: BoxFit.fill,
),
),
),
],
);
TextField(
inputFormatters: [
WhitelistingTextInputFormatter(RegExp("[a-zA-Z]")),//只允许输入字母
],
),
TextField(
inputFormatters: [WhitelistingTextInputFormatter.digitsOnly],//只允许输入数字
),
TextField(
inputFormatters: [
WhitelistingTextInputFormatter(RegExp("[0-9.]")),//只允许输入小数
],
)
flutter listView顶部空白
ListView头部有一段空白区域,是因为当ListView没有和AppBar一起使用时,头部会有一个padding,为了去掉padding,可以使用MediaQuery.removePadding:
@override
Widget build(BuildContext context){
return MediaQuery.removePadding(
removeTop: true,
context: context,
child: ListView.separated(
itemCount: hotMovies.length,
itemBuilder: (context, index){
return HotMovieItemWidget(hotMovies[index]);
},
separatorBuilder: (context, index){
return Divider(
height: 1,
color: Colors.black26,
);
},
),
);
}
ListView无法充满全面,顶部会有一个导航栏宽度的缝隙,应该是自适应屏幕造成,这时只需将ListView的padding属性,设置为:EdgeInsets.only(top: 0) 即可解决问题。
Flutter 获取某个控件的坐标
1.首先先需要对控件进行渲染
初始化GlobalKey :GlobalKey anchorKey = GlobalKey();
2.在需要测量的控件的下面添加key:
child: Text("点击弹出悬浮窗",
style: TextStyle(fontSize: 20),
key: anchorKey
),
3.获取控件的坐标:
RenderBox renderBox = anchorKey.currentContext.findRenderObject();
var offset = renderBox.localToGlobal(Offset.zero);
控件的横坐标:offset.dx
控件的纵坐标:offset.dy
如果想获得控件正下方的坐标:
RenderBox renderBox = anchorKey.currentContext.findRenderObject();
var offset = renderBox.localToGlobal(Offset(0.0, renderBox.size.height));
控件下方的横坐标:offset.dx
控件下方的纵坐标:offset.dy
//// var offset = renderBox.localToGlobal(Offset(0.0, renderBox.size.height));
// var offset = renderBox.localToGlobal(Offset(0.0, 0.0));
// RenderBox getBox = context.findRenderObject();
GestureDetector(
onTapUp: (TapUpDetails tapUp) {
// print(" onTapUp ============= {localOffset.dx} dy ={toRemove.contains(item)}");
// if(toRemove.contains(item)){
// item.isValidValue = false;
// }
// });
this._mineProductionList.removeWhere((value) {
// print(" onTapUp removeWhere 2 ============= {value.toString()}");
return this.toRemove.contains(value);
});
// print(" onTapUp remove ============= ${_mineProductionList.length}");
_minePainter = MinePainter( mineProductionList: this._mineProductionList);
});
},
child: CustomPaint(
painter: _minePainter,
size: Size(double.infinity, double.infinity),
),
),
# [No internet in Android Virtual device [duplicate]](https://stackoverflow.com/questions/54622647/no-internet-in-android-virtual-device)
Maybe you have DNS address problem. Try these steps on MacOs:
Open "System Preferences"
Click on "Network"
Select the network which your computer is connected and click on "Advanced"
Select "DNS", Select the "+" button, type "8.8.8.8"
Select "Ok" and "Apply"
After that close the emulator and start it again.
我们需要实现组件绑定观察者(WidgetsBindingObserver)抽象类,使用组件(Widgets)图层绑定注册的类的接口,我们会覆盖它的App生命周期状态发生改变(didChangeAppLifecycleState)方法,其返回值类型是应用生命周期状态(AppLifecycleState)类。
应用生命周期状态(AppLifecycleState)类有几个常量,分别是:等待(AppLifecycleState.inactive)、暂停(AppLifecycleState. paused)和恢复(AppLifecycleState. resumed)。
class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
@override
void initState() {
// 在当前页面放一个观察者。
WidgetsBinding.instance.addObserver(this);
super.initState();
}
@override
void dispose() {
// 移除当前页面的观察者。
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
// 当App生命周期状态为恢复时。
if (state == AppLifecycleState.resumed) {
getClipboardContents();
}
}
/// 使用异步调用获取系统剪贴板的返回值。
getClipboardContents() async {
// 访问剪贴板的内容。
ClipboardData clipboardData = await Clipboard.getData(Clipboard.kTextPlain);
// 剪贴板不为空时。
if (clipboardData != null && clipboardData.text.trim() != '') {
String _name = clipboardData.text.trim();
// 淘口令的正则表达式,能判断类似“¥123456¥”的文本。
if (RegExp(r'[\uffe5]+.+[\uffe5]').hasMatch(_name)) {
// 处理淘口令的业务逻辑。
showDialog<Null>(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return CupertinoAlertDialog(
title: Text('淘口令'),
content: Text(_name),
);
},
);
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Demo 主页'),
),
);
}
在Flutter中,事件流是“向上”传递的,而状态流是“向下”传递的
),重定向这一流程的共同父元素是State。
[https://book.flutterchina.club/](https://book.flutterchina.club/)
To maximize container's width, set its width to MediaQuery.of(context).size.width
double.infinity有时候会报错
MediaQuery.of(context).size.height*0.35
Iterable<Widget> listTitles=items.map((String item){//将items的内容设置给Adapter
return buildListTile(context,item);
});
最后将Adapter设置给ListVIew:
new ListView(
children: listTitles.toList(),//添加ListView控件
)
在StatefulWidget中State通过widget.获取StatefulWidget里面的全局变量
项目结构:
class test extends StatefulWidget{
var title;
_MainUi createState() =>new _MainUi();
}
class _MainUi extends State<test> {
@override
Widgetbuild(BuildContext context) {
widget.title;//获取全局变量
}
}
// Binary data
List<int> postData = <int>[...];
await dio.post(
url,
data: Stream.fromIterable(postData.map((e) => [e])), //create a Stream<List<int>>
options: Options(
headers: {
HttpHeaders.contentLengthHeader: postData.length, // set content-length
},
),
);
意思就是dio实例就是指new 的一个dio对象;拦截器是应该在发送请求之前就设置好的,不应该在请求的同时去修改。另外:
不建议动态改变baseUrl,一般一个dio实例对应一个baseUrl,如果要用该实例去发送其它域的请求可以在url参数中写完整链接,这样便会忽略baseUrl, 详情可以看example下面的示例。如:
var dio = Dio();
dio.options.baseUrl = "http://www.dtworkroom.com/doris/1/2.0.0/";
// 下面使用完整域名访问会忽略baseUrl
Response response = await dio.get("https://www.google.com/");
print(response.data);
如果非要动态改baseUrl,可以通过dio.options.baseUrl直接修改,没必要通过拦截器去设置。
GlobalKey _scaffold = Global Key();
Scaffold(
key: _scaffold
);
...
showDialog(context: _scaffold.currentContext)
[flutter] 手机拍摄的照片base64编码后显示方向不正确
flutter读取照片然后压缩并base64编码时发现了这个问题.
后来了解了一下:
手机拍摄的照片会把拍摄的相机信息存到exif信息里面
所以读取手机拍摄的照片后要先从exif信息里面读取下拍摄时的手机角度,然后旋转下照片才能得到正常照片
我的解决方法:
https://pub.dartlang.org/packages/exifdart
pubspec.yml的dependencies节点添加:
dependencies: exifdart: ^0.7.0+3
这是一个用来读取exif信息的库
使用方法:
import 'package:exifdart/exifdart.dart'; /// 获取一个照片要旋转多少度才是正常方向Future<int> getImageRotateAngular(List<int> bytes) async { Map<String, dynamic> tags = await readExif(MemoryBlobReader(bytes)); var orientation = tags['Orientation']; //获取该照片的拍摄方向 switch (orientation) { case 3: return 180; case 6: return 90; case 8: return -90; default: return 0; }}
获取到旋转方向后把图片旋转下再压缩编码就可以了!
通过 ClipRect 的 clipper 属性,我们可以对显示区域进行限制,接下来自定义一个
CustomClipper
class CustomRect extends CustomClipper<Rect> {
@override
Rect getClip(Size size) {
Rect rect = Rect.fromLTRB(0.0, 0.0, size.width, size.height);
return rect;
}
@override
bool shouldReclip(CustomRect oldClipper) {
return false;
}
}
这样,我们就可以把显示内容限制在 rect 的范围内
等待加载image后更新布局
class _MyHomePageState extends State<MyHomePage> {
ui.Image image;
bool isImageloaded = false;
void initState() {
super.initState();
init();
}
Future <Null> init() async {
final ByteData data = await rootBundle.load('images/lake.jpg');
image = await loadImage(new Uint8List.view(data.buffer));
}
Future<ui.Image> loadImage(List<int> img) async {
final Completer<ui.Image> completer = new Completer();
ui.decodeImageFromList(img, (ui.Image img) {
setState(() {
isImageloaded = true;
});
return completer.complete(img);
});
return completer.future;
}
Widget _buildImage() {
if (this.isImageloaded) {
return new CustomPaint(
painter: new ImageEditor(image: image),
);
} else {
return new Center(child: new Text('loading'));
}
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Container(
child: _buildImage(),
)
);
}
}
运行flutter的时候显示警告
Waiting for another flutter command to release the startup lock
复制代码当项目异常关闭,或者android studio用任务管理器强制关闭,下次启动就会出现上面的一行话,
此时需要打开 flutter/bin/cache/lockfile,删除就行了
或者直接用下面的命令:rm ./flutter/bin/cache/lockfile
有时候注意自己的目录是否不一样,我的rm ./flutter/flutter/bin/cache/lockfile
如果上述方法还是不行,则删除 flutter SDK中 bin/cache 文件夹,然后在cmd命令行执行flutter upgrade
import 'package:flutter/services.dart';
// 强制只能竖屏
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
iOS白屏
<key>io.flutter.embedded_views_preview</key>
<string>YES</string>
如果需要访问 HTTP 网页,还需要添加以下。
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Flutter 有两种可选参数,一种是基于名称,一种是基于位置
{}是基于名称的,[]是基于位置的
1.基于名称
Person(String name,{String gender,int number}){
}
//调用
Person('zhangsan')//可不传
Person('zhangsan',gender:'man',number:20);
2.基于位置
Person(String name,[String gender,int number]){
}
//调用
Person('zhangsan')//可不传
Person('zhangsan','man',20);//必须按照顺序传递
Flutter闭包
makeFunction(int xx){
print(xx);
int callback(yy){
print(yy);
return xx + yy;
};
return callback;
}
//调用
int result = makeFunction(110)(10);
//result = 120
其实就是函数嵌套一个返回的函数
Flutter函数做参数传递
void bbb(){
print('bbbbb');
}
aaa(int num,Function func){
print(num);
if(num == 111) {
bbb();
}
}
//调用
aaa(111,bbb);
//或者
aaa(111, (){
print('kkkk');
});