flutter安卓开发概述


Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。这篇文章主要讲述了使用flutter开发的大致流程及可能遇到的问题,关于flutter开发的详细文档,可以参考 flutter中文网

UI构建

传统的android开发使用 xml 布局文件构建UI,而flutter使用树形结构的多个widget完成UI构造。大致过程为

  1. 构建页面框架,一般继承 Stateless Widget,并使用Scaffold构建AppBarDrawer
  2. 构建页面的内容
    1. 不会更新的控件继承Stateless Widget
    2. 会更新的控件继承Stateful Widget
  3. 编写页面其它逻辑,并在需要的地方调用setState方法

下面是一个简单的例子(实际效果以运行后的结果为准):

在这里插入图片描述

/// 构建一个页面,继承 Stateless Widget
class NotesInSheetPage extends StatelessWidget {
  final Sheet sheet;
  const NotesInSheetPage(this.sheet, {Key key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    NoteInSheetBody body = NoteInSheetBody();
    return Scaffold(
        appBar: AppBar( // AppBar
            title: Text(sheet.title), 
            titleSpacing: 0,
            leading: UtilUI.backButton(() => Navigator.pop(context)),  // IconButton with Icons.arrow_back
            actions: <Widget>[UtilUI.syncWidget(body.doSync)]), // an IconButton has Icons.sync icon
        body: body,
        floatingActionButton: Builder(builder: (BuildContext context) { // floating add button
          return UtilUI.floatAddButton(() {
            UtilUI.showSnackBar(context, Text(ResString.pleaseExpect));
          });
        }));
  }
}
class NoteInSheetBody extends StatefulWidget {
  final _NoteInSheetBodyState state = _NoteInSheetBodyState();
  @override
  _NoteInSheetBodyState createState() => state;
  void doSync() => state.syncNotes();
}

class _NoteInSheetBodyState extends State<NoteInSheetBody> {
  List<Note> noteList;
  bool syncing = false;

  @override
  Widget build(BuildContext context) {
    return Container(
        padding: EdgeInsets.all(ResPadMargin.common1),
        child: ListView.builder(
          itemCount: noteList != null ? noteList.length : 0,
          itemBuilder: noteListItemBuilder,
        ));
  }
  /// builder of note list item
  Widget noteListItemBuilder(BuildContext context, int index) {
    Note tmpNote = noteList[index];
    return GestureDetector(
        child: NotesListItem(
          tmpNote.title,
          Util.formatTime(tmpNote.lastModTime),
          tmpNote.content,
        ),
        onTap: () => Navigator.push(
            context, MaterialPageRoute(builder: (context) => NoteDetailPage(tmpNote))),
        onLongPressStart: (LongPressStartDetails details) =>
            _showDeleteMenu(details.globalPosition));
  }
  /// show popup menu
  void _showDeleteMenu(Offset pos) {}
  /// sync all notes
  void syncNotes() async {
    // ... fetch data from server
    setState(() => noteList = tmpNoteList);
  }
}

可以看到,flutter中使用Widget来构建UI, Scaffold, AppBar, ListView 等都是常用的控件。

Stateless Widget与Stateful Widget

Stateless Widget 即无状态的控件,它可以用来构建UI中永远不会改变的部分,如下面的 Text 文本显示控件:

new Text('I like Flutter!', style: new TextStyle(fontWeight: FontWeight.bold));

Stateful Widget 则是有状态的,对于与用户交互或者从网络获取数据的控件,应当使用它来构建。对于 Stateful Widget 来说,状态 State 是对其能够变化部分的抽象。当一个 Stateful Widget 需要更新(状态发生改变)的时候,可以通过 setState(void Function()) 更新它的状态,如上面的 NoteInSheetBody
值得一提的是,flutter建议尽量将状态下移,即只让真正需要更新的控件是Stateful的,因为在setState的时候会重绘这个控件,若顶层控件为Stateful的,那么整个页面都会重新绘制。

遇到的一些问题

构建UI固然简单,但与逻辑结合起来的时候则可能遇到许多问题,如下是我遇到的一些问题即解决方法:

  1. Scaffold.of() called with a context that does not contain a Scaffold
    这个问题一般是因为调用 Scaffold.of(context) 的时候其中的 context 是一个 ScaffoldBuildContext,如下所示
     Widget build(BuildContext context) {
       return Scaffold(
         appBar: AppBar(title: Text(sheet.title)),
         body: Container(),
         floatingActionButton: FloatingActionButton(
             child: Icon(Icons.add), 
             onPressed: () => Scaffold.of(context).showSnackBar(content: Text('test')),
       ));
     }
    
    其中浮动按钮 floatingActionButtononPressed 方法中使用了此 Scaffoldcontext,因此会产生错误。解决方法有两个,一种是将调用Scaffold.of()的控件分离到另一个类中,另一种是使用一个builder,如下所示:
     Widget build(BuildContext context) {
       return Scaffold(
         appBar: AppBar(title: Text(sheet.title)),
         body: Container(),
         floatingActionButton: Builder(builder: (BuildContext context) {
           return FloatingActionButton(
             child: Icon(Icons.add), 
             onPressed: () => Scaffold.of(context).showSnackBar(content: Text('test')),
           );
         }));
     }
    
  2. 长按弹出菜单
    GestureDetector提供的onLongPress方法并未包含触摸位置信息,因此需要使用onLongPressStart方法,其函数签名为void Function(LongPressStartDetails details),其中的detail参数的globalPosition属性包含了触摸屏幕的坐标信息。如下为简化后的代码:
     /// builder of note list item
     Widget noteListItemBuilder(BuildContext context, int index) {
       return GestureDetector(
           // ... other content
           onLongPressStart: (LongPressStartDetails details) =>
               showMenu(
                 context: context,
                 // generate a PopupMenuEntry with given texts and values
                 items: UtilUI.popUpMenuEntry([ResString.delete], [1]), 
                 position: RelativeRect.fromLTRB(
                   details.globalPosition.dx,
                   details.globalPosition.dy,
                   details.globalPosition.dx, 
                   details.globalPosition.dy,
                 )).then((value) => print('pressed: $value'));
     }
    
    如代码所示,showMenu方法可以在给定的position处显示一个弹出菜单,并在菜单点击或取消时调用then中的方法。
  3. flutter build apk的注意事项
    1. 一定要确定manifest文件中的权限是否声明
    2. 可以使用参数 --target-platform android-arm 生成特定版本的apk
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值