bool get isInDebugMode {
bool inDebugMode = false;
return inDebugMode;
}
Future main() async {
FlutterError.onError = (FlutterErrorDetails details) async {
if (isInDebugMode) {
// In development mode simply print to console.
FlutterError.dumpErrorToConsole(details);
} else {
// In production mode report to the application zone to report to
// Sentry.
Zone.current.handleUncaughtError(details.exception, details.stack);
}
};
//自定义错误提示页面
ErrorWidget.builder = (FlutterErrorDetails flutterErrorDetails) {
return Scaffold(
appBar: AppBar(
title: Text("错误页面"),
),
body: Center(
child: Text("Custom Error Widget"),
));
};
runZonedGuarded(
() => runApp(MyApp()),
zoneSpecification: ZoneSpecification(
print: (Zone self, ZoneDelegate parent, Zone zone, String line) {collectLog(line); // 捕获所有的print
},
),
(error, stack) {
var details = makeDetails(obj, stack);
reportErrorAndLog(details);
},
);
}
extension CustomThemeData on ThemeData {
Color get primaryBlue => brightness == Brightness.dark ? Color(0xFF228AEE) : Color(0xFF1880EE);
// ignore: non_constant_identifier_names
Color get primaryBlue_outline => brightness == Brightness.dark ? Color(0xFF228AEE) : Color(0xFF1880EE);
}
TextPainter painter = TextPainter(text: TextSpan(text: v, style: fieldTextStyle), textDirection: TextDirection.ltr);
painter.layout();
print(painter.width);
final GlobalKey _textFieldKey = GlobalKey();
Text("aaa", key: _textFieldKey,);
double inputWidth = _textFieldKey.currentContext.size.width;
获取屏幕Size,使用 MediaQuery.of(context).size
flutter 中图片的使用需要在pubspec.yaml中 声明,代码如下:
图片放在和pubspec.yaml同级的iamges文件夹中
flutter:
uses-material-design: true
assets:
- images/
//图片使用时的代码,使用在pubspec.yaml声明时的图片路径+图片的名字
//Contanier 的 decoration中可以设置image decoration ,使用 DecorationImage对象,可以直接加载 asserts 中 的image 资源
Container(
……
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("images/warnIcon.png")),
) )
)
class BannerNotification extends Notification {
BannerNotification(this.isShow,
{this.type,
this.tip,
……
});
String tip;
int type;
……
}```
- 在父widget的build中 return NotificationListener 对象,代码如下:
```dart
@override
Widget build(BuildContext context) {
return NotificationListener<BannerNotification>(
onNotification: (notification) {
//处理逻辑 的代码
……
return ture;
},
child: Text("aaa")
);
}
}
notification.dispatch(context);
Builder(
builder: (context) {
return MaterialButton(onPressed: (){
return MaterialButton(onPressed: (){
……
},);
},);
},
)
class BaseBody extends StatelessWidget {
final Widget child;
static BuildContext _buildContext;
static void switchBanner(BannerNotification notification) {
notification.dispatch(_buildContext);
}
@override
Widget build(BuildContext context) {
return NotificationListener(
onNotification: (notification) {
……
//逻辑代码
return true;
},
child: Stack(
children: [
Builder(
builder: (context) {
BaseBody._buildContext = context;
return widget.child;
},
),
],
),
);
}
}
@override
void dispose() {
_controller?.dispose();
super.dispose();
}
//若还是出现controller 没有dispose ,可以在deactivate 中执行 controller?.dispose();
@override
void deactivate() {
_controller?.dispose();
super.deactivate();
}
完全自定义 TextField中的contentPadding,代码如下:
TextField(
scrollPadding: EdgeInsets.zero,//默认是20,现在改成0
decoration: InputDecoration(
contentPadding: EdgeInsets.only(left: 12.px, right: 2.px, top: 12.px, bottom: 12.px),//自定义 contentPadding
isDense: true,//为true时 ,TextField的vertical方向没有space,默认是false。
),
)
TextField 中 keyboardType 属性中 TextInputType.visiblePassword 为设置键盘模式为输入密码模式,但是 输入的字符是可见的,若想不可见,可以设置 TextField的obscureText 属性为 ture,则输入字符不可见。代码如下:
TextField(
keyboardType: TextInputType.visiblePassword,
autofocus: true,
……
),
//动画的控制类,如动画的播放,暂停,反转,重复。
_controller = AnimationController(
duration: Duration(milliseconds: 250), vsync: provider)
..addStatusListener((status) {
//动画状态的监听,completed,dissmiss,forward,reverse
}
})..addListener(() {
//在此获取动画的当前值。
var a = _controller.value; //a的值为0到1之间
var b = _animation.value;/ /b的值在Tween定义的begin与end之间。
});
//CurvedAnimation 定义运动变化的速率,curve: 决定动画的差值器
var curved = CurvedAnimation(parent: _bannerController, curve: Curves.easeOut)
// Tween 中定义动画的取值区间,数值可以是任意类型,如 Color,Offset,double等。
_animation = Tween(begin: Offset(0, -1), end: Offset(0, 0)).animate(curved);
}
//weight:执行时长占总时长的权重
//TweenSequenceItem : 定义不同的动画
Animation animation=TweenSequence([
TweenSequenceItem(tween: Tween(begin: 0,end: 100),weight: 20),
TweenSequenceItem(tween: CurveTween(curve:Curves.easeIn ),weight: 80)
]).animate( _controller);
Gradient颜色渐变的方式:LinearGradient(线形变换);RadialGradient(放射状变化);SweepGradient(扇形变化)
blendMode:颜色混合模式
tileMode:指定在begin,end之外的区域颜色如何渲染,repeated:重复使用区域之内的渲染颜色;clamp:按照接近的区域内的颜色;mirror:以镜像模式重复区域内的渲染颜色
demo如下:
ShaderMask(
shaderCallback: (Rect bounds) {
return RadialGradient(
center: Alignment.bottomCenter,
radius: _gradientValue,
colors: [_iconColor, _loadingColor],
stops: [_gradientValue, 1],
tileMode: TileMode.repeated,
).createShader(bounds);
},
blendMode: BlendMode.srcATop,
child: Icon(
_icon,
size: _iconSize,
),
)
ShaderMask(
shaderCallback: (Rect bounds) {
return SweepGradient(
center: Alignment.bottomCenter,
startAngle: 0,
endAngle: pi,
tileMode: TileMode.clamp,
colors: [_iconColor, _loadingColor],
stops: [_gradientValue, 1],
).createShader(bounds);
},
blendMode: BlendMode.dstOver,
child: Icon(
_icon,
size: _iconSize,
),
)
LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
tileMode: TileMode.mirror,
colors: [_iconColor, _loadingColor],
stops: [_gradientValue, 1],
).createShader(bounds);
},
blendMode: BlendMode.dstATop,
child: Icon(
_icon,
size: _iconSize,
),
);
Container 中设置阴影效果
的style 有 outer,inner,normal,solid
demo
Container(
……
decoration: BoxDecoration(
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Color(0xFF000000),//阴影的颜色
blurRadius: 10.px,//阴影的半径
offset: Offset(2.px, 2.px),//shadow 对于 box的偏移量
spreadRadius: 10.px,//box扩展的大小
),
],
),
)
box shadow的原理
//ui.shadow
static double convertRadiusToSigma(double radius) {
return radius * 0.57735 + 0.5;//阴影的逻辑像素计算公式
}
//根据传入的半径,计算阴影的逻辑像素
double get blurSigma => convertRadiusToSigma(blurRadius);
Paint toPaint() {
return Paint()
..color = color
//使用 MaskFilter.blur 实现paint的阴影效果
..maskFilter = MaskFilter.blur(BlurStyle.normal, blurSigma);
} }
问题:BlurStyle的类型有 normal,inner,outer,solid ;在 box shadow中 发现使用 inner,outer,normal的效果一样,没有区别。
widget中textbaseline的类型分为:alphabetic:按照英文字符的baseline对齐;ideographic:按照中文字符的baseline对齐。
Text.rich :富文本widget,类似于android的SpannableString:
Text.rich(
//必须设置的属性,在里面设置展示的字符串和样式
TextSpan(
text: number,//最开始展示的字符串
children: [//里面依次设置需要的Span
//设置相应的字符串和样式
TextSpan(
text: "\t$unit",
style: _unitStyle,
),
//设置其他类型的widget
WidgetSpan(child: Padding(padding: EdgeInsets.all(10),child: FlatButton(),))
],
style: _numberStyle,//最开始展示的字符串和样式
),
);
const Text.rich(
this.textSpan, {
Key key,
this.style,//设置默认的样式
this.textAlign,//字符串的对齐方式
this.textDirection,//字符串的方向
this.softWrap, //超出控件长度的字符串是否截断
this.overflow,//超出控件长度的字符串的处理方式
this.textScaleFactor,//字体的缩放
this.maxLines,//最大行数
})
……
Visibility(
child: CustomPaint(
size: size,//设置绘制区域的大小。如果有 child,则忽略该参数,且绘制区域为 child 的尺寸
foregroundPainter:_ArcPainter(),//前景绘制
painter : _ArcPainter(),//背景绘制
isComplex:false,//是否复杂的绘制,如果是,Flutter 会应用一些缓存策略来减少重复渲染的开销。默认 false
willChange:false,//和 isComplex 配合使用,当启用缓存时,该属性代表在下一帧中绘制是否会改变。默认 false
child: ... //CustomPaint 是可以包含一个子节点的
),
)
//自定义的paint
class _ArcPainter extends CustomPainter {
……
//重写paint方法,使用 Canvas对象绘制图形
@override
void paint(Canvas canvas, Size size) {
//设置paint对象的属性
Paint backgroundPaint = Paint()
backgroundPaint.style = PaintingStyle.stroke;
backgroundPaint.strokeWidth = arcSize.px;
backgroundPaint.color = backgroundColor;
var start = pi / 6.2 + pi;
var swap = 2 * pi / 3;
//设置 绘制扇形的矩形范围
var rect = Rect.fromLTRB(2.px, size.height / 12, size.width, size.height);
canvas.drawArc(rect, start, swap, false, backgroundPaint);
//其他的绘制方法
// 绘制弧线
// drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint)
// 绘制图片
// drawImage(Image image, Offset p, Paint paint)
// 绘制圆
// drawCircle(Offset c, double radius, Paint paint)
// 绘制线条
// drawLine(Offset p1, Offset p2, Paint paint)
// 绘制椭圆
// drawOval(Rect rect, Paint paint)
// 绘制文字
// drawParagraph(Paragraph paragraph, Offset offset)
// 绘制路径
// drawPath(Path path, Paint paint)
// 绘制点
// drawPoints(PointMode pointMode, List points, Paint paint)
// 绘制Rect
// drawRect(Rect rect, Paint paint)
// 绘制阴影
// drawShadow(Path path, Color color, double elevation, bool transparentOccluder)
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
//返回 true 会进行重绘,否则就只会绘制一次。你可以通过一些条件判断来决定是否每次绘制,这样能够节约系统资源。
return false;
}
}
flutter中正则表达式使用方式
var reg = new RegExp(r"^[\u4E00-\u9FA5A-Za-z0-9_]+$");//匹配中英文,数字,\u4E00-\u9FA5 表示中文,
bool has = reg.hasMatch("aaa");//aaa 是否匹配规则
//RegExp 中还有其他的匹配的方法,如:allMatches(),firstMatch() 等
注意:RegExp的规则参数中是以 r 来开始的,后面是匹配规则的字符串,匹配规则是以^ 开始 $ 结束
flutter中 对变量的值进行限制时 可以使用 assert 实现 ,demo如下:
assert(widget.barStyleWrapper != null, "barStyleBuilder must not null");
flutter 中打开 bottom sheet 有2种方式:
```dart
// showBottomSheet
showBottomSheet(
context: context,
builder: (context) {
return Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height - 60,
decoration: BoxDecoration(color: Colors.red),
child: MaterialButton(
onPressed: () {
Navigator.pop(context);//dismiss
},
child: Text("click"),
),
);
});
//showModalBottomSheet
showModalBottomSheet(context: context,
isScrollControlled: true,//为true 时,当 内部的widget的高度大于等于屏幕高度时可以滚动显示,为false时,内部的widget的展示的高度只能是小于等于屏幕的一半
enableDrag: false,//是否可以下拉消失
isDismissible: false,//点击外部区域是否消失
barrierColor: Colors.yellow,//设置自定义widget以为区域的颜色
useRootNavigator: true,
routeSettings: RouteSettings(name:"aa"),// 这个和useRootNavigator还没用到过
builder: (context){
return Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height - 60,
decoration: BoxDecoration(color: Colors.green),
child: MaterialButton(
onPressed: () {
Navigator.pop(context);/dismiss
},
child: Text("click"),
),
);
});
```
相同:
- 1. 都在通过一个package 下面
- 2. 都有 backgroundColor 属性,但设置了没作用,不知道为啥
- 3. 在 自定义的widget里面都可以使用Navigator.pop(context); dismiss 掉 bottom
在 scaffold中使用 输入框,界面上移时,会超出状态栏,可以将 scaffold中的resizeToAvoidBottomInset 属性设成false来限制,而且 ,设置resizeToAvoidBottomInset=false后 使用 MediaQuery.of(context).viewInsets.bottom获取 键盘的高度时,可以获取到正确的值,若为true时,获取的值一直为0
flutter中判断键盘是否弹出,即获取 键盘高度,demo如下:
class _BottomSheetPanelState extends State
with WidgetsBindingObserver {
MediaQueryData _queryData;
@override
void initState() {
super.initState();
/添加监听
WidgetsBinding.instance.addObserver(this);
}
@override
Widget build(BuildContext context) {
_queryData = MediaQuery.of(context);
return Scaffold(
resizeToAvoidBottomInset: false, //在 scaffold中坚挺 键盘的高度必须将resizeToAvoidBottomInset设成false
body: Stack(
……
);
}
//重写WidgetsBindingObserver中的didChangeMetrics方法
@override
didChangeMetrics() {
super.didChangeMetrics();
//监听界面变化回调
WidgetsBinding.instance.addPostFrameCallback((_) {
_fromDrag = false;
_isDrag = true;
//_queryData.viewInsets.bottom获取 键盘弹起的距离,即键盘的高度
if (_queryData.viewInsets.bottom == 0) {
//收起键盘
} else {
//显示键盘
}
});
}
……
}
dart 中 " " 表示 一行字符串,""" “”" 表示 段落式的字符串
可以超出父Widget的size约束的Widget :OverflowBox,SizedOverflowBox,使用 demo 如下:
Container(
padding: EdgeInsets.all(20),
width: 50,
alignment: Alignment.bottomRight,
height: 50,
decoration: BoxDecoration(color: Colors.red),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedOverflowBox(
alignment: Alignment.center,
size: Size(10, 10),
child: Container(
width: 50,
height: 100,
decoration: BoxDecoration(color: Colors.green),
),
),
],
))
Center(
child: Container(
width: 50,
height: 50,
decoration: BoxDecoration(color: Colors.red),
child: OverflowBox(
alignment: Alignment.center,
minWidth: 0,
minHeight: 0,
maxWidth: 100,
maxHeight: 200,
child: Container(
width: 10,
height: 200,
decoration: BoxDecoration(color: Colors.green.withAlpha(100)),
),
)),
)
PageStorage.of(context).writeState(context, data);
保存数据PageStorage.of(context).readState(context);
读取保存的数据,在其他的生命周期方法如 build中读取也是可以的,在didChangeDependencies的好处时,该方法是在 widget 初始化之后直接调用的。而且每次刷新不会重复执行。class TestDemo extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _TestDemoState();
}
class _TestDemoState extends State {
final List _list = [
Page1(),
PageStoreDemo(
key: PageStorageKey("key1"),//子Widget 必须设置 key ,并且key的类型必须是PageStorageKey
),
PageStoreDemo(
key: PageStorageKey("key2"),
),
PageStoreDemo(
key: PageStorageKey("key3"),
),
];
int _index = 0;
final PageStorageBucket _bucket = PageStorageBucket();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Test"),
),
// 父Wiget 必须为PageStorage
body: PageStorage(
child: _list[_index],
bucket: _bucket,
),
bottomNavigationBar: BottomNavigationBar(
onTap: (index) {
setState(() {
_index = index;
});
},
currentIndex: _index,
selectedItemColor: Colors.red,
items: [
BottomNavigationBarItem(icon: Icon(Icons.add), label: "page1"),
BottomNavigationBarItem(icon: Icon(Icons.call), label: "page2"),
BottomNavigationBarItem(icon: Icon(Icons.input), label: "page3"),
BottomNavigationBarItem(icon: Icon(Icons.padding), label: "page4"),
],
),
);
}
}
class Page1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: GestureDetector(
child: Container(
width: 50,
constraints: BoxConstraints(maxHeight: 100, maxWidth: 100),
height: 50,
decoration: BoxDecoration(color: Colors.red),
child: SizedBox(
width: 100,
height: 100,
child: Container(
decoration: BoxDecoration(color: Colors.green.withAlpha(100)),
),
)),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return PageStoreDemo(
key: PageStorageKey("key1"),
);
}));
},
),
);
}
}
class PageStoreDemo extends StatefulWidget {
PageStoreDemo({PageStorageKey key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return _PageStoreState();
}
}
class _PageStoreState extends State<PageStoreDemo> {
String _text = "0";
int _count = 0;
@override
Widget build(BuildContext context) {
return Center(
child: Row(
children: [
Text(_text),
MaterialButton(
onPressed: () {
setState(() {
_text = "${_count++}";
});
//子widget中保存需要的数据
PageStorage.of(context).writeState(context, _text);
},
child: Text("click"),
)
],
),
);
}
@override
void didChangeDependencies() {
//读取保存的数据
if (PageStorage.of(context)?.readState(context) != null) {
_text = PageStorage.of(context).readState(context);
}
super.didChangeDependencies();
}
}
使用 AutomaticKeepAliveClientMixin 保存 页面的状态,demo如下:
但是这种方式子Widget的容器必须是 TabBarView或者PageView
//父widget
_controller = TabController(length: _list.length,vsync: this);
Scaffold(
appBar: AppBar(
title: Text("Test"),
),
// 父Wiget 必须为PageStorage
body: TabBarView(
children: _list,
controller:_controller,
),
bottomNavigationBar: BottomNavigationBar(
onTap: (index) {
setState(() {
_index = index;
});
_controller.animateTo(_index);
},
currentIndex: _index,
selectedItemColor: Colors.red,
items: [
BottomNavigationBarItem(icon: Icon(Icons.add), label: "page1"),
BottomNavigationBarItem(icon: Icon(Icons.call), label: "page2"),
BottomNavigationBarItem(icon: Icon(Icons.input), label: "page3"),
BottomNavigationBarItem(icon: Icon(Icons.padding), label: "page4"),
],
),
);
//子widget
class _PageStoreState extends State
with AutomaticKeepAliveClientMixin {
String _text = "0";
int _count = 0;
@override
void initState() {
super.initState();
print("_PageStoreState---initState");
}
@override
Widget build(BuildContext context) {
super.build(context);
return Center(
child: Row(
children: [
Text(_text),
MaterialButton(
onPressed: () {
setState(() {
_text = "${_count++}";
});
// //子widget中保存需要的数据
// PageStorage.of(context).writeState(context, _text);
},
child: Text("click"),
)
],
),
);
}
@override
bool get wantKeepAlive => true;
}
flutter中调用native 的扫描二维码的sdk,使用flutter打开相机并预览,然后将摄像头读取到的数据传给native的sdk。
在flutter中添加camera的dependencies,如下:
dependencies:
camera:
path_provider:
path:
……
在 main中初始化 WidgetsFlutterBinding 获取 camera的cameraDescription 对象,如下:
class Camera {
static CameraDescription cameraDescription;
}
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
Camera.cameraDescription = (await availableCameras()).first;//获取设备摄像头的信息
……
}
自定义 二维码扫描的widget,在里面初始化 CameraController对象,打开摄像头,并使用 CameraPreview 进行预览,如下:
class _QRCodeDemoState extends State {
CameraController _controller;
Future _future;
@override
void initState() {
//使用在main中获取的CameraDescription对象初始化 CameraController
//第二个参数指定图像的分辨率,ResolutionPreset.high 表示 高分辨率的图像
_controller = CameraController(Camera.cameraDescription, ResolutionPreset.high);
_future = _controller.initialize();//初始化并打开 摄像头
super.initState();
//在 初始化摄像头的完成后 ,使用CameraController的 startImageStream方法获取 摄像头传输的实时图像数据
_cameraInit.then((_){
_cameraInit.then((_) {
if (!mounted) {
return;
}
setState(() {});
}).whenComplete(() {
compute(send(_cameraController, context), ""); });
} }
}
//该函数必须是顶级函数或者static 函数
send(CameraController _cameraController, BuildContext context) async {
_cameraController.startImageStream((image) async {
await FordBasicChannel.sendCameraData().callNative({
"data": image.planes.map((e) => e.bytes).toList(), //获取到的图像像素点数据,
"width": image.width, //图像的size
"height": image.height //图像的size
}).then((value) {
if (value != null) {
//扫描结果处理
}
}, onError: (error) {
FordBody.switchBanner(
BannerNotification(true, type: BannerType.error, tip: "扫描出错,请退出重试"));
});
});
}
在 native 中 使用 method channel 接受图像的数据,android端代码如下:
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
super.configureFlutterEngine(flutterEngine)
transientDataProvider.save(FlutterInitUseCase())
val util = ScanUtils()
MethodChannelInvokeHandler(MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "scan_test"))
.setMethodCallHandler(MethodChannel.MethodCallHandler { call, result ->
if ("scan_test" == call.method) {
val flutterData: List<ByteArray>? = call.argument<List<ByteArray>>("data")//图像的数据点
val width = call.argument<Int>("width") ?: 200
val height = call.argument<Int>("height") ?: 200
util.scan(flutterData, width, height, result)
}
})
}
获取到数据之后,需要将数据进行处理,startImageStream 方法的输出为 CameraImage,有 4 个属性: 图像格式, 高度, 宽度以及 planes ,planes 包含图像具体信息。
class CameraImage {
final ImageFormat format;
final int height;
final int width;
final List planes;
}
注意在不同平台上的图像格式并不相同:Android: android.graphics.ImageFormat.YUV_420_888;iOS: kCVPixelFormatType_32BGRA (在 2.8.0 版本中的格式为 kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, 后来在 4.0.0 版本中又改回为 32BGRA。)
由于图像格式不同,输出的 CameraImage 在 Android 和 iOS 端包含的信息也不一样:Android: planes 含有三个字节数组,分别是 Y、U 和 V plane;iOS:planes 只包含一个字节数组,即图像的 RGBA 字节。
参考的链接 在 Flutter 中实现实时图像检测
Android 的解析以及调要zxing代码如下:
fun scan(flutterData: List<ByteArray>?, width: Int, height: Int, result: MethodChannel.Result) {
flutterData?.apply {
//对图像数据进行处理
val Y: ByteBuffer = ByteBuffer.wrap(this[0])
val U: ByteBuffer = ByteBuffer.wrap(this[1])
val V: ByteBuffer = ByteBuffer.wrap(this[2])
val Yb: Int = Y.remaining()
val Ub: Int = U.remaining()
val Vb: Int = V.remaining()
val data = ByteArray(Yb + Ub + Vb)
Y[data, 0, Yb]
V[data, Yb, Vb]
U[data, Yb + Vb, Ub]
// 进行zxing 二维码解析
val task = QRParse(result)//创建 zxing解析二维码的线程
task.execute(ScanData(data, width, height))
}
}
// 调用zxing的线程
class QRParse(private val result: MethodChannel.Result) : AsyncTask<ScanData, Void, String>() {
val utils = ScanParseUtils()
override fun doInBackground(vararg scanData: ScanData): String {
//utils.processData 是 对zxing进行封装,对外提供的方法
val scanResult: String? = utils.processData(scanData[0].data, scanData[0].width, scanData[0].height, false)
return scanResult ?: ""
}
override fun onPostExecute(scanResult: String?) {
super.onPostExecute(scanResult)
result.success(scanResult ?: "")
}
}
// 对 flutter 传入数据的封装
data class ScanData(val data: ByteArray, val width: Int, val height: Int) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ScanData
if (!data.contentEquals(other.data)) return false
if (width != other.width) return false
if (height != other.height) return false
return true
}
override fun hashCode(): Int {
var result = data.contentHashCode()
result = 31 * result + width
result = 31 * result + height
return result
}
}
ios的解析代码如下:
const FlutterStandardTypedData* typedData = args[@"bytesList"][0];
uint8_t* in = (uint8_t*)[[typedData data] bytes];
float* out = interpreter->typed_tensor(input);
for (int y = 0; y < height; ++y) {
const int in_y = (y * image_height) / height;
uint8_t* in_row = in + (in_y * image_width * image_channels);
float* out_row = out + (y * width * input_channels);
for (int x = 0; x < width; ++x) {
const int in_x = (x * image_width) / width;
uint8_t* in_pixel = in_row + (in_x * image_channels);
float* out_pixel = out_row + (x * input_channels);
for (int c = 0; c < input_channels; ++c) {
out_pixel[c] = (in_pixel[c] - input_mean) / input_std;
}
}
}
flutter 路由
在 MaterialApp 类中 的 routes 属性和 home属性:
routes:管理项目中的路由,数据为 MapNavigator.of(context).pushNamed
等方法名中包含name的方法时传入的字符串就是该值,或者native 指定 默认路由,使用 FlutterActivity 重写getInitialRoute()
方法返回的字符串或者使用 flutter engine对象的flutterEngine.navigationChannel.setInitialRoute()
方法字符串参数都是该值。
home 属性 ,在没有指定初始化页面路由时,加载的是home属性指定的页面。
路由跳转
Navigator.of(context) .pushNamed
参数:
var args = ModalRoute.of(context).settings.arguments;
Navigator.of(context).push()
,参数为Route对象,一般使用MaterialPageRoute 对象,路由返回
Navigator.of(context) .pop(value)
直接返回上一个页面,有一个可选参数:value,表示 返回到上一个页面的数据,在 push方法的返回值future对象中获取。Navigator.of(context).popUntil((route) { return true }))
连续返回页面,popUntil的参数为 function 类型,其返回值为bool类型,当function 返回ture时,停止页面关闭。function的参数为 Route 类型,可以通过 Route 对象的 RouteSettings 对象做页面判断。