Flutter基础(纯新手笔记)
1、有状态和无状态的控件
// StatefulWidget 有状态(更新)的控件 class MyText extends StatefulWidget {} // 由两个类组成如:Test类和_Test类 // StatelessWidget 无状态(更新)的控件 class MyText extends StatelessWidget {}
// 有状态控件写法如下
class Test extends StatefulWidget {
const Test({super.key, required this.params});
final Map? params;
@override // 复写,拿父类有的方法来重写类似render
State<Test> createState() => _Test(); // 创建一个_Test类
}
class _Test extends State<Test> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return 控件;
}
}
2、颜色码的使用注意事项:
// 不能使用三位缩写颜色码,如#333必须改为#333333;
3、交互控件
GestureDetector // 常用回调 // onTap 点击 // onTapDown 按下 // onTapUp 抬起 // onTapCancel 点击取消事件,比如点击某个按钮后未抬起手指状态下移出按钮 // onDoubleTap 双击 // onDoubleTapDown 双击按下 // onDoubleTapUp 双击抬起 // onDoubleTapCancel 双击取消
// onLongPress 长按
4、超出屏幕解决方法
SingleChildScrollView // 注意事项:只适合稍微超出的内容,长列表不适合,长列表使用listview控件 // 其他注意事项:横向溢出一般用Expanded
5、Row控件
行内子控件宽度自适应:mainAxisSize: MainAxisSize.min
排列方式:mainAxisAlignment: MainAxisAlignment.spaceBetween;crossAxisAlignment: CrossAxisAlignment.start
6、外边框、圆角写法
decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(12)), color: HexColor('#FFFFFF') ) // 12圆角白色底的矩形框
shape: BoxShape.circle, // 圆
// 圆角图片(背景)
Container(
width: 100,
height: 100,
decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(12.w),
shape: BoxShape.circle,
image: DecorationImage(
image: NetworkImage('xxxxxxxxx'),
fit: BoxFit.cover)
)
),
)
// border属性: strokeAlign: BorderSide.strokeAlignCenter,确定边框是在哪个位置,内边框/外边框/居中边框;
7、日历控件的使用
SfDateRangePicker // 注意事项:日历本身属性更改才会刷新 // 解决方法:headerHeight: headerHeight,设置该属性,赋值为headerHeight+/-0.01,
// onViewChange方法内的数据更改需要在Future内,不然页面会报错,如下
void _onViewChange(dateRangePickerViewChangedArgs) {
Future.microtask(() {
setState(() {
xx = xxx;
});
});
}
8、数组map遍历方法
// 遍历值 list.map((item) => { return item; }).toList(); // 遍历索引 list.asMap().keys.map((index) => { return list[index]['xxx']; }).toList();
9、window问题,不能通过window获取屏幕相关属性,获取页面宽度用以下代替
MediaQuery.of(context).size.width
10、字符串的一些方法
判断是否包含某字符串:str.contains('xx');
截取字符串(从右往左截取两个字符):str.substring(0, str.length - 2);
11、滑块的使用及注意事项
Container( margin:xxx, width: xxx, height: 20, // 这里是可点击拖拽滑动的区域,不能跟轨道高度一样小,不然会导致滑动非常不灵敏 child: SliderTheme( data: SliderThemeData( trackHeight: 6, // 轨道高度 xxxxxx ), child: Slider( value: xxx, onChanged: (value) { setState(() { xxx = xxxx; }); }, onChangeEnd: (value) { setState(() { xxx = xxxx; }); }, // 轨道值范围 min: 0, // 开始值 max: 5, // 结束值 // divisions: 5, // 轨道要分几段 activeColor: HexColor('xxx'), // 滑动过的颜色 inactiveColor: HexColor('xxx'), // 未滑动过的颜色 ), ), )
12、拉起相机拍照或者获取相册获取照片
需要用到插件:image_picker
测试:web端、Android端
import 'dart:io'; // 文件操作用到
import 'package:image_picker/image_picker.dart';
var imgPath;
// 调取相机或者相册
void _takePhoto() async { var img = await ImagePicker().pickImage(source: ImageSource.camera); // 拉起相册:ImageSource.gallery setState(() { imgPath = File(img!.path); }); }
// 将图片展示 Image.network( // android端不支持 imgPath.path, ),
Image.file( // web端不支持 imgPath, ),
// 保存图片到本地用到的插件
import 'package:gallery_saver/gallery_saver.dart';
import "package:universal_html/html.dart" as html;
// 保存图片到本地 void _savePhoto() async {
// 测试gif动图下载后为png格式 if (kIsWeb) { // web端,下载完之后需要用户保存到手机
// 方法一,file文件 final a = html.AnchorElement(href: imgPath.path); a.download = '图片.png'; a.click(); a.remove();
// 方法二,base64
Uint8List bytes = base64.decode(imgBase64);
final blob = html.Blob([bytes], 'image/jpeg');
final url = html.Url.createObjectUrlFromBlob(blob);
final a = html.AnchorElement(href: url);
a.download = '图片.png';
a.click();
a.remove();
html.Url.revokeObjectUrl(url);
Fluttertoast.showToast( msg: '保存成功!', ); } else { GallerySaver.saveImage(File(imgPath!.path).path).then((value) { Fluttertoast.showToast( msg: '保存成功!', ); }); } }
13、使用动画的一些注意事项
// 会重复播放的控制器
late final AnimationController _repeatController;
// 线性动画
late final Animation<double> _animation;
@override void initState() { super.initState(); _repeatController = AnimationController( duration: const Duration(microseconds: 2000), vsync: this) ..addListener(() {});
// 动画持续时间是 3秒,此处的this指 TickerProviderStateMixin或SingleTickerProviderStateMixin
_repeatController = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
)..repeat(); // 设置动画重复播放
// 创建一个从0到360弧度的补间动画 v * 2 * π
_animation = Tween<double>(begin: 0, end: 1).animate(_repeatController);
}
class _xxxState extends State<xxx> with SingleTickerProviderStateMixin { ...
RotationTransition(
turns: _animation,
child: xxx,
)
}
14、上下固定,中间滚动的布局
Scaffold( backgroundColor: HexColor('#F7F7F7'), body: Column( children: [ Expanded( child: SingleChildScrollView( child: Column(children: []), )), XXX(children: []) // 底部固定 ]));
15、路由跳转及获取路由传参
// 跳转,带参 Navigator.push( context, MaterialPageRoute( maintainState: true, builder: (_) { return getRouter("路由")({ "xxx": xxxxx, // 参数 }); })); // 获取参数 @override void initState() { super.initState(); print('当前路由参数是:${widget.params}'); }
// 返回上一级路由
Navigator.of(context).pop();
16、子控件撑满父控件的高度
IntrinsicHeight(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [Expanded(child:
Stack(children:[
子控件1,// 有高度,撑开父控件
子控件2,// 与父控件高度一致
]))]
)
)
17、图片的一些使用
// base64图片 Image.memory( base64Decode(base64imgStr), ); // file图片
Image.network( // android端不支持 imgPath.path, );
Image.file( // web端不支持 imgPath, );
// 工程内静态图片
Image.asset(
'resource/images/...',
width: 100,
fit: BoxFit.fitWidth, // 图片根据宽度高度自适应
);
18、渐变字
ShaderMask( shaderCallback: (bounds) => LinearGradient( begin: Alignment.centerLeft, end: Alignment.centerRight, colors: [HexColor(渐变色码1), HexColor(渐变色码2)]) .createShader( Rect.fromLTWH(0, 0, bounds.width, bounds.height), ), child: Text( '渐变字', style: TextStyle( color: Colors.white, fontSize: 18, ), ), );
19、弹窗
// 其中一种写法,AlertDialog实现 void showModalFun(BuildContext context) { showDialog( context: context, builder: (context) { return AlertDialog( // 去掉默认样式,背景和padding backgroundColor: const Color.fromRGBO(0, 0, 0, 0), contentPadding: EdgeInsets.zero, content: Container() // 弹窗 ); }); } // 打开弹窗 showModalFun(context); // 关闭弹窗 Navigator.of(context).pop();
// 另一种方法,Dialog实现,宽高不受限的写法
showDialog( context: context, builder: (context) { return StatefulBuilder(builder: (context, setState) { return UnconstrainedBox( // 抵消弹窗原有的约束,再自己设置宽度 constrainedAxis: Axis.vertical, child: SizedBox( width: MediaQuery.of(context).size.width, child: Dialog( // 去掉默认样式 backgroundColor: const Color.fromRGBO(0, 0, 0, 0), insetPadding: EdgeInsets.zero, child: xxxx))); // 真正的弹窗 }); });
20、路由相关
// 回到第一个路由(打开的首页) Navigator.of(context).popUntil((route) => route.isFirst); // 带参数路由 Navigator.push( context, MaterialPageRoute( maintainState: true, builder: (_) { return getRouter("/xxx/xxx")( {"a": xxx, "b": xxx}); })); // 带回调的路由 () async { await Navigator.push( context, MaterialPageRoute( maintainState: true, builder: (_) { return getRouter("/xxx/xxx")( {"a": xxx, "b": xxx}); })); // 路由跳转结束回到页面后的操作 // ... } // 回到上一路由,也可用于关闭弹窗 Navigator.of(context).pop();
21、以页面高度自适应,页面高度不够展示给一个最小高度页面可滚动
SingleChildScrollView( child: Stack( alignment: Alignment.topCenter, children: [ Container( width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height, // 拿页面高度 constraints: BoxConstraints(minHeight: 600.w), // 页面高度低于600赋值600, 不够展示了就只能滚动展示 ), Positioned(top: 100.w, bottom: 60.w, child: content()) // 需要自适应高度的控件 ], ));
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 【.NET】调用本地 Deepseek 模型
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
2021-08-31 React字符串关键字替换样式