Flutter小技巧

2,748 阅读3分钟

1.Android设置沉浸式

// SystemUiOverlayStyle用于覆盖系统navigationBar,statusBar等样式
// SystemChrome 控制操作系统图像界面与操作系统交互类
// SystemChrome.setPreferredOrientations: 设置横竖屏
// SystemChrome.setSystemUIOverlayStyle():// 设置状态栏和导航栏目
// SystemChrome.setEnabledSystemUIOverlays() 对状态栏的操作:是否隐藏状态栏或者三大金刚键,常用于键盘弹出时候,隐藏三大金刚键
if (Platform.isAndroid) {
    SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle(statusBarColor: Colors.transparent);
    SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
  }

2.precacheImage

在Flutter中,加载本地图片会存在一个加载过程。比如点击图标做图标的切换时,那么首次会发生闪动的情况。尤其是做类似引导页这类需求是,通过左右滑动切换图片时会发生比较明显的白屏一闪而过。

解决方法:使用precacheImage,将图像预存到图像缓存中,如果图像稍后被使用,他会被加载得更快。

precacheImage(AssetImage("images/logo.png"), context); 

3.设置状态栏的颜色

// 方式一:
appbar: AppBar(
    title: Text('.....'),
    brightness: Brightness.dark,
);
// 方式二:(不使用AppBar的情况下,只需要在最外层包裹一下)
Widget build(BuildContext context) {
    return AnnotatedRegion<SystemUiOverlayStyle> (
        value: SystemUiOverlayStyle.light,
        child: ....
    )
}

4.Stack子组件设置宽高不起作用

// red不起作用
child: Container(
height: 300,
width: 300,
color: Colors.blue,
child: Stack(
  children: <Widget>[
    Positioned.fill(
      child: Container(
        height: 100,
        width: 100,
        color: Colors.red,
      ),
    )
  ],
),
),
// 解决方案:使用Center,Align或者UnconstrainedBox包裹一下,例如:
Positioned.fill(
  child: Align(
    child: Container(
      height: 100,
      width: 100,
      color: Colors.red,
    ),
  ),
)

5.default value of optional parameter must be constant

在类构造函数的时候经常会报以上异常。异常信息提示:可选参数必须为常量

class BarrageItem extends StatefulWidget {
    BarrageItem({
        this.text,
        this.duration = Duration(seconds: 3)
    });
}
// 可以这样修复
const Duration _kDuration = Duration(seconds: 3);
class BarrageItem extends StatefulWidget {
    BarrageItem({
        this.text,
        this.duration = _kDuration
    });
}

6.TextField动态获取焦点和失去焦点

// 定义
_focusNode = FocusNode();
TextField(
    focusNode: _focusNode,
    ...
);
// 获取焦点
FocusScope.of(context).requestFocus(_focusNode);
// 失去焦点
_focusNode.unfocus();

7.创建一个圆角Button

方式一:使用FlatButtonRaiseButton

shap: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(18.0),
    side: BorderSide(color: Colors.red)
)

方式二:使用ClipReact

ClipReact(
    borderRadius: Border.circle(40),
    child: RaiseButton(
        onPressed:(){},
        child: Text("Button")
    )
)

方式三:使用ButtonTheme

ButtonTheme(
    shape: RoundedRectangleRorder(borderRadius: BorderRadius.circle(20),
    child: RaiseButton(
        onPressed:() {},
        child: Text("....")
    )
)

8.让Button充满父组件

// 方式一
SizedBox.expand(
    child: RaisedButton(...)
)
// 方式二
ConstrainedBox(
    contraints: const BoxConstraints(minWidth: double.infinity),
    child: RaisedButton(...)
)
// 方式三
ButtonTheme(
    minWidth: double.infinity,
    child: MaterialButton(
        onPress:(){},
        child: Text('Raised Button')
    )
)

9.如何给图片添加圆角

// 方式一
ClipRRect(
    borderRadius: borderRadius.circular(8.0),
    child: Image.network(...)
)
// 方式二
CircleAvatar(
    radius: 20,
    backgroundImage: NetworkImage('....')
)
// 方式三
ClipOval(
    child: Image.network(
        "image_url",
        height: 100,
        width: 100,
        fit: BoxFit.cover
    )
)
// 方式四
Container(
    width: 100.0,
    height: 150.0,
    decorartion: BoxDecoration(
        image: DecorationImage(
            fit: BoxFit.cover,
            image: NetworkImage('....')
        ),
        borderRadius: BorderRadius.all(Radius.circular(8.0)),
        color: Colors.redAccent
    )
)

10.如何去掉TextField的下划线

InputDecoration(
    border: InputBorder.none,
    hintText: 'Username'
)

11.显示/隐藏控件

// 控件一
Opacity(
    opacity: 0,
    child: ...
)
// 控件二
Visibility(
    visible:false,
    child: ...
)
// 控件三
offstage(
    offstage: true,
    child:...
)

12.如何截取Android的返回按键并处理

WillPopScope用于处理是否离开当前页面。询问用户是否退出

Widget build(BuildContext context) {
    return WillPopScope(
        onWillPop: () async => {
            return showDialog(
                context: context,
                builder: (context) =>
                    AlertDialog(
                    title: Text("你确定要退出吗"),
                    actions: <Widget>[
                        RaisedButton(
                            child: Text('退出'),
                            onPressed ()=>Navigator.of(context).pop(true),
                        ),
                        RaisedButton(
                            child: Text('取消'),
                            onPressed ()=>Navigator.of(context).pop(false),
                        ),
                    ]
                )
            )
        },
        child: Scaffold(child: ...)
    )
}

快速点击两次退出

DateTime _lastQuitTime;
Widget build(BuildContext context) {
    return WillPopScope(
        onWillPop: () async => {
            if(_lastQuitTime == null ||  DateTime.now().difference(_lastQuitTime).inSeconds > 1) {
                Scaffold.of(context)
                    .showSnackBar(SnackBar(content: Text('再按一次 Back 按钮退出')));
                _lastQuitTime = DateTime.now();
                return false;
            } else {
                print('退出');
                Navigator.of(context).pop(true);
                return true;
            }
        },
        child: Scaffold(child: ...)
    )
}

13.设置AppBar的Height

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Example',
      home: Scaffold(
        appBar: PreferredSize(
          preferredSize: Size.fromHeight(50.0), // here the desired height
          child: AppBar(
            // ...
          )
        ),
        body: // ...
      )
    );
  }
}

14.Column的子控件底部剧中,左对齐

return Column(
  crossAxisAlignment: CrossAxisAlignment.center,
  mainAxisSize: MainAxisSize.max,
  mainAxisAlignment: MainAxisAlignment.end,
  children: <Widget>[
      //your elements here
  ],
);

15.Illegal percent encoding in URI

使用fluro路由为下一个页面传参,且参数为中文时报的这个错误。解决方案,在url中使用中文参数时候,使用Uri.encodeComponent转下码就可以了。

Application.router.navigateTo(context, '$routeName?title=${Uri.encodeComponent(title)} ', transition: TransitionType.inFromRight);

16.Vertical viewport was given unbounded height

这个bug是在column中使用ListView出现的。解决方案是在ListView组件中使用shrinkWrap属性就可以了

return Column(
    children: <Widget>[
      ListView.builder(
          itemCount: _datalist.length,
          itemBuilder: _buildItem,
          // 使用shrinkWrap
          shrinkWrap: true,
      ),
    ],
  );

17.隐藏键盘

// 隐藏键盘
FocusScope.of(context).unfocus();

TabBar切换导致重建build问题(不知道为啥我的没用,先记录着,后续改进)

// 解决方案:设置PageStorageKey
var _onePageKey = PageStorageKey("onePages");
var _twoPageKey = PageStorageKey("twoPages");
TabBarView(
    controller: _controller,
    children: <Widget>[
        _onePage(_onePageKey),
        _twoPage(_twoPageKey)
    ]
)