Flutter使用小技巧一(持续更新…)
当mian
的路径被修改后ios/Runner.xcodeproj
运行失败,需要修改ios/Flutter/Generated.xcconfig
配置
// This is a generated file; do not edit or check into version control.
FLUTTER_ROOT=/Users/sensiwu/Library/Android/FlutterSDK/flutter
FLUTTER_APPLICATION_PATH=/Users/sensiwu/AndroidProject/Tangren/FlutterDemo/multi_language
COCOAPODS_PARALLEL_CODE_SIGN=true
**FLUTTER_TARGET=lib/i18n/main.dart //main路径**
FLUTTER_BUILD_DIR=build
FLUTTER_BUILD_NAME=1.0.0
FLUTTER_BUILD_NUMBER=1
EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386
DART_OBFUSCATION=false
TRACK_WIDGET_CREATION=false
TREE_SHAKE_ICONS=false
PACKAGE_CONFIG=.packages
Image.asset 设置宽高无效
使用Container
包裹Image.asset
发现image
宽高跟随Container
而不是设置的大小,解决办法是:再使用Center
包裹Image.asset
Container(
height: 60.0,
width: 60.0,
margin: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(10.0)),
child: Center(
child: Image.asset(
iconUrl,
width: 30,
height: 30,
),
),
),
导航返回的拦截
可以使用WillPopScope
const WillPopScope({
...
required WillPopCallback onWillPop,
required Widget child
})
onWillPop
是一个回调函数,当用户点击返回按钮时被调用(包括导航返回按钮及Android物理返回按钮)。该回调需要返回一个Future
对象,如果返回的Future
最终值为false
时,则当前路由不出栈(不会返回);最终值为true
时,当前路由出栈退出。我们需要提供这个回调来决定是否退出。
import 'package:flutter/material.dart';
class WillPopScopeTestRoute extends StatefulWidget {
@override
WillPopScopeTestRouteState createState() {
return WillPopScopeTestRouteState();
}
}
class WillPopScopeTestRouteState extends State<WillPopScopeTestRoute> {
DateTime? _lastPressedAt; //上次点击时间
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
if (_lastPressedAt == null ||
DateTime.now().difference(_lastPressedAt!) > Duration(seconds: 1)) {
//两次点击间隔超过1秒则重新计时
_lastPressedAt = DateTime.now();
return false;
}
return true;
},
child: Container(
alignment: Alignment.center,
child: Text("1秒内连续按两次返回键退出"),
),
);
}
}
对于Dialog
也同样适用
_showBottomDialog(
BuildContext context, String title, List<SelectContentModel> list) {
context: context,
isDismissible: false,
builder: (context) {
return WillPopScope(
onWillPop: () async => false,
child: WidgetSelectListView(
name: title,
...),
);
});
}
}
手动退出最后一个page
SystemNavigator.pop()
TextField光标问题
如果设置了 controller.text
,并且在 onChanged
发生变化的时候 通过 setState(){}
重新更新 text
,那就会使输入框状态发生变化,光标会回到初始位置。
static textEditingController(String text) {
return TextEditingController.fromValue(TextEditingValue(
// 设置内容
text: text ?? "",
// 保持光标在最后
selection: TextSelection.fromPosition(TextPosition(
affinity: TextAffinity.downstream, offset: text == null ? 0 : text.length))));
}
TextField切换焦点、隐藏默认下划线
切换焦点
如果一个页面有多个输入框,想实现输入结束,点键盘的完成直接去下一个焦点输入,
首先需要给各个输入框设置focusNode
,然后完成的回调事件是onEditingComplete
,
通过FocusScope.of(context).requestFocus(_focusNodePwd);
设置新的焦点
隐藏默认下划线
在 InputDecoration
里 有个 border
将值设为InputBorder.none
顶部状态栏高度可通过 MediaQueryData.fromWindow(window).padding.top
获取,底部 *** 作栏高度可通过 MediaQueryData.fromWindow(window).padding.bottom
获取,在需要依附底部按钮的地方,一定需要设置 margin bottom
*** 作栏高度,否则按钮会被遮挡,在 iPhone 没有 home 键的机型上可以看出来
只需在组件的最外层包裹IgnorePointer
或者AbsorbPointer
组件,_ignoring
为变量,可以通过服务器下发,为 true 时,表示禁用。
@override
Widget build(BuildContext context) {
return IgnorePointer(
ignoring: _ignoring,
child: MaterialApp(
title: 'Flutter Fly',
theme: ThemeData(
primaryColor: Colors.white,
accentColor: Color(0xFF5DC782),
backgroundColor: Color(0xFFF2F2F2)),
routes: Routes.routes,
initialRoute: Routes.home_page,
debugShowCheckedModeBanner: false,
),
);
}
置灰App
在组件最外层包裹ColorFiltered
,使整个 App 变为灰色,包括图片资源。
@override
Widget build(BuildContext context) {
return ColorFiltered(
colorFilter: ColorFilter.mode(Colors.grey, BlendMode.color),
child: MaterialApp(
title: 'Flutter Fly',
theme: ThemeData(
primaryColor: Colors.white,
accentColor: Color(0xFF5DC782),
backgroundColor: Color(0xFFF2F2F2)),
routes: Routes.routes,
initialRoute: Routes.home_page,
debugShowCheckedModeBanner: false,
),
);
}
底部导航切换导致重建问题
导致重建的代码
Widget _currentBody;
@override
Widget build(BuildContext context) {
return Scaffold(
body: _currentBody,
bottomNavigationBar: BottomNavigationBar(
items: <BottomNavigationBarItem>[
...
],
onTap: (index) {
_bottomNavigationChange(index);
},
),
);
}
_bottomNavigationChange(int index) {
switch (index) {
case 0:
_currentBody = OnePage();
break;
case 1:
_currentBody = TwoPage();
break;
case 2:
_currentBody = ThreePage();
break;
}
setState(() {});
}
使用IndexStack
优化
int _currIndex;
@override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: _currIndex,
children: <Widget>[OnePage(), TwoPage(), ThreePage()],
),
bottomNavigationBar: BottomNavigationBar(
items: <BottomNavigationBarItem>[
...
],
onTap: (index) {
_bottomNavigationChange(index);
},
),
);
}
_bottomNavigationChange(int index) {
setState(() {
_currIndex = index;
});
}
pageView
的切换导致重建的问题解决办法如下
class DashboardScreen extends GetView<DashboardController> {
const DashboardScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
controller: controller.pageController,
onPageChanged: (index) => controller.onChangePage(index),
children: const [
KeepAliveWrapper(child: HomeScreen()),
KeepAliveWrapper(child: CloudScreen())
],
),
bottomNavigationBar: Obx(
() => BottomNavBar(
currentIndex: controller.selectIndex.value,
onSelected: (index) => controller.changePage(index),
),
));
}
}
class KeepAliveWrapper extends StatefulWidget {
final bool keepAlive;
final Widget child;
const KeepAliveWrapper({Key? key, this.keepAlive = true, required this.child})
: super(key: key);
@override
_KeepAliveWrapperState createState() => _KeepAliveWrapperState();
}
class _KeepAliveWrapperState extends State<KeepAliveWrapper>
with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
super.build(context);
return widget.child;
}
@override
void didUpdateWidget(covariant KeepAliveWrapper oldWidget) {
if (oldWidget.keepAlive != widget.keepAlive) {
updateKeepAlive();
}
super.didUpdateWidget(oldWidget);
}
@override
bool get wantKeepAlive => widget.keepAlive;
}
Waiting for another flutter command to release the startup lock
mac/liunx
killall -9 dart
windows
taskkill /F /IM dart.exe
方法2:
删除 flutter SDK
的目录下/bin/cache/lockfile
文件
FractionallySizedBox
控件获取父控件的大小并乘以百分比:
MediaQuery.of(context).size.width * 0.5
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)