美文网首页Flutter
Flutter Go 源码分析(二)

Flutter Go 源码分析(二)

作者: Hedgehog___ | 来源:发表于2019-06-05 16:47 被阅读86次
    (5) AppPage()基础页面

    AppPage是整个App的入口,在这里实现Tabbar、SearchBar等基础控件。
    在分析AppPage页面之前先说一下Scaffold这个widget,这里我们可以把它理解为页面,类似OC里面的UIViewController:

        this.appBar, //横向水平布局,通常显示在顶部(*)
        this.body, // 内容(*)
        this.floatingActionButton, //悬浮按钮,就是上图右下角按钮(*)
        this.floatingActionButtonLocation, //悬浮按钮位置
        //悬浮按钮在[floatingActionButtonLocation]出现/消失动画
        this.floatingActionButtonAnimator, 
        //在底部呈现一组button,显示于[bottomNavigationBar]之上,[body]之下
        this.persistentFooterButtons,
        //一个垂直面板,显示于左侧,初始处于隐藏状态(*)
        this.drawer,
        this.endDrawer,
        //出现于底部的一系列水平按钮(*)
        this.bottomNavigationBar,
        //底部持久化提示框
        this.bottomSheet,
        //内容背景颜色
        this.backgroundColor,
        //弃用,使用[resizeToAvoidBottomInset]
        this.resizeToAvoidBottomPadding,
        //重新计算布局空间大小
        this.resizeToAvoidBottomInset,
        //是否显示到底部,默认为true将显示到顶部状态栏
        this.primary = true,
        //
        this.drawerDragStartBehavior = DragStartBehavior.down,
    

    AppPage:

    Widget build(BuildContext context) {
        var db = Provider.db;
    
        return new Scaffold(
          appBar: new AppBar(title: buildSearchInput(context)),
          body: new TabBarView(controller: controller, children: <Widget>[
            new FirstPage(),
            new WidgetPage(db),
            new CollectionPage(),
            FourthPage()
          ]),
          bottomNavigationBar: Material(
            color: const Color(0xFFF0EEEF), //底部导航栏主题颜色
            child: SafeArea(
              child: Container(
                height: 65.0,
                decoration: BoxDecoration(
                  color: const Color(0xFFF0F0F0),
                  boxShadow: <BoxShadow>[
                    BoxShadow(
                      color: const Color(0xFFd0d0d0),
                      blurRadius: 3.0,
                      spreadRadius: 2.0,
                      offset: Offset(-1.0, -1.0),
                    ),
                  ],
                ),
                child: TabBar(
                    controller: controller,
                    //tab标签的下划线颜色
                    indicatorColor: Theme.of(context).primaryColor,
                    
                    // labelColor: const Color(0xFF000000),
                    indicatorWeight: 3.0,
                    //labelcolor 选中的
                    labelColor: Theme.of(context).primaryColor,
                    //labelColor: Colors.green,
                    unselectedLabelColor: const Color(0xFF8E8E8E),
                    tabs: myTabs),
              ),
            ),
          ),
        );
    

    主要是通过AppBar-->buildSearchInput()(搜索框)、body-->TabBarView()(页面)、bottomNavigationBar-->TabBar()(tabbar)三部分组成,下面我们来依次拆解。

    1. buildSearchInput
      buildSearchInput函数里是创建的SearchInput对象:
      构造函数:
      final getResults;//获取搜索内容函数
    
      final ValueChanged<String> onSubmitted;//没有用到
    
      final VoidCallback onSubmitPressed;//没有用到
    
      SearchInput(this.getResults, this.onSubmitted, this.onSubmitPressed);//构造函数
    

    build函数除了MaterialSearchInput之外都是一些基础wiget布局,其他不做阐述,我们来看一下MaterialSearchInput
    build函数:

    Widget build(BuildContext context) {
        final TextStyle valueStyle = Theme.of(context).textTheme.subhead;
    
        return new InkWell(
          onTap: () => _showMaterialSearch(context),
          child: new FormField<T>(
            key: _formFieldKey,
            validator: widget.validator,
            onSaved: widget.onSaved,
            autovalidate: autovalidate,
            builder: (FormFieldState<T> field) {
              return new InputDecorator(
                isEmpty: _isEmpty(field),
                decoration: new InputDecoration(
                  labelText: widget.placeholder,
                  border: InputBorder.none,
                  errorText: field.errorText,
                ),
                child: _isEmpty(field)
                    ? null
                    : new Text(
                        widget.formatter != null
                            ? widget.formatter(field.value)
                            : field.value.toString(),
                        style: valueStyle),
              );
            },
          ),
        );
      }
    

    可以看到这搜索框是有一个FormField
    来实现的,这里实例化对象的时候只用到了getResultsplaceholder我暂时只对这两个做说明,其他属性如果有感兴趣的同学可以自行去了解。
    接下来我们主要研究_showMaterialSearch,也就是点击之后跳转的搜索页面。

    _showMaterialSearch(BuildContext context) {
        Navigator.of(context)
            .push(_buildMaterialSearchPage(context))
            .then((dynamic value) {
          if (value != null) {
            _formFieldKey.currentState.didChange(value);
            widget.onSelect(value);
          }
        });
      }
    

    _showMaterialSearch-->_MaterialSearchPageRoute-->MaterialSearchbuild函数:

    Widget build(BuildContext context) {
        var results =
            (widget.results ?? _results).where((MaterialSearchResult result) {
          if (widget.filter != null) {
            return widget.filter(result.value, _criteria);
          }
          //only apply default filter if used the `results` option
          //because getResults may already have applied some filter if `filter` option was omited.
          else if (widget.results != null) {
            return _filter(result.value, _criteria);
          }
    
          return true;
        }).toList();
    
        if (widget.sort != null) {
          results.sort((a, b) => widget.sort(a.value, b.value, _criteria));
        }
    
        results = results.take(widget.limit).toList();
    
        IconThemeData iconTheme =
            Theme.of(context).iconTheme.copyWith(color: widget.iconColor);
    
        return new Scaffold(
          appBar: new AppBar(
            leading: widget.leading,
            backgroundColor: widget.barBackgroundColor,
            iconTheme: iconTheme,
            title: new TextField(
              controller: _controller,
              autofocus: true,
              decoration:
                  new InputDecoration.collapsed(hintText: widget.placeholder),
              style: Theme.of(context).textTheme.title,
              onSubmitted: (String value) {
                if (widget.onSubmit != null) {
                  widget.onSubmit(value);
                }
              },
            ),
            actions: _criteria.length == 0
                ? []
                : [
                    new IconButton(
                        icon: new Icon(Icons.clear),
                        onPressed: () {
                          setState(() {
                            _controller.text = _criteria = '';
                          });
                        }),
                  ],
          ),
          body: buildBody(results),
          );
      }
    

    组成部分有两部分

    • 1)AppBar(主要是textfield)
      用一个TextEditingController来配合监听输入框的文字变化
    _controller.addListener(() {
          setState(() {
            _criteria = _controller.value.text;
            if (widget.getResults != null) {
              _getResultsDebounced();
            }
          });
        });
    

    通过构造函数传进来的getResults来去数据库获取搜索结果。

    Timer _resultsTimer;
      Future _getResultsDebounced() async {
        if (_results.length == 0) {
          setState(() {
            _loading = true;
          });
        }
    
        if (_resultsTimer != null && _resultsTimer.isActive) {
          _resultsTimer.cancel();
        }
        //延迟400毫秒再执行
        _resultsTimer = new Timer(new Duration(milliseconds: 400), () async {
          if (!mounted) {
            return;
          }
    
          setState(() {
            _loading = true;
          });
    
          var results = await widget.getResults(_criteria);
    
          if (!mounted) {
            return;
          }
    
          if (results != null) {
            setState(() {
              _loading = false;
              _results = results;
            });
          }
        });
      }
    
    • 2)body(buildBody)
      最外层代码不讲解了,我都注释好了:
    Widget buildBody(List results) {
        if (_criteria.isEmpty) {//如果没有搜索关键字则显示历史记录
          return History();
        } else if (_loading) {//正在搜索显示加载框
          return new Center(
              child: new Padding(
                  padding: const EdgeInsets.only(top: 50.0),
                  child: new CircularProgressIndicator()
              )
          );
        }
        if (results.isNotEmpty) {//如果有搜索结果就显示搜索列表
          var content = new SingleChildScrollView(
              child: new Column(
                children: results
              )
          );
          return content;
        }
        return Center(child: Text("暂无数据"));//这个是有搜索关键字而没有搜索结果的时候显示暂无数据
      }
    

    看一下搜索历史记录页面:

    • History()
      build函数:
    Widget build(BuildContext context) {
        //获取历史记录的widget list
        List<Widget> childList = buildChips(context);
        if (childList.length == 0) {//如果没有历史记录 
          return Center(
            child: Text("当前历史面板为空"),
          );
        }
        return Column(//有历史记录
          children: <Widget>[
            Container(//头部 历史搜索文字
              alignment: Alignment.centerLeft,
              padding: EdgeInsets.fromLTRB(12.0, 12, 12, 0),
              child: InkWell(
                onLongPress: () {//长按情况搜索历史记录
                  searchHistoryList.clear();
                },
                child: Text('历史搜索'),
              ),
            ),
            Container(//搜索历史列表
              padding: EdgeInsets.only(left: 10),
              alignment: Alignment.topLeft,
              child: Wrap(
                spacing: 6.0, // gap between adjacent chips
                runSpacing: 0.0, // gap between lines
                children: childList
              ),
            )
          ],
        );
      }
    

    获取历史记录的widget list 方法buildChips

    buildChips(BuildContext context) {
        List<Widget> list = [];//存储搜索列表widget
        List<SearchHistory> historyList = searchHistoryList.getList();//获取搜索记录数据(SearchHistory)
        print("historyList> $historyList");
        Color bgColor = Theme.of(context).primaryColor;
        historyList.forEach((SearchHistory value) {//遍历历史记录数据 转成widget装入list
    
          Widget icon = CircleAvatar(
            backgroundColor: bgColor,
            child: Text(
              value.name.substring(0, 1),
              style: TextStyle(color: Colors.white),
            ),
          );
          if (WidgetName2Icon.icons[value.name] != null) {
            icon = Icon(WidgetName2Icon.icons[value.name], size: 25);
          }
    
          list.add(
            InkWell(
              onTap: () {//跳转
                Application.router.navigateTo(context, "${value.targetRouter}", transition: TransitionType.inFromRight);
              },
              child: Chip(
                avatar: icon,
                label: Text("${value.name}"),
              ),
            )
          );
        });
        return list;
      }
    

    代码都已做了详细注释,不做过多解释了。

    • 搜索结果列表
      首先我们看results数据:
    Widget buildSearchInput(BuildContext context) {
        return new SearchInput((value) async {
          if (value != '') {
            List<WidgetPoint> list = await widgetControl.search(value);
    
            return list
                .map((item) => new MaterialSearchResult<String>(
                      value: item.name,
                      icon: WidgetName2Icon.icons[item.name] ?? null,
                      text: 'widget',
                      onTap: () {
                        onWidgetTap(item, context);
                      },
                    ))
                .toList();
          } else {
            return null;
          }
        }, (value) {}, () {});
      }
    

    不难看出results里面装的是MaterialSearchResult的实例对象,MaterialSearchResultbuild 方法:

    Widget build(BuildContext context) {
    
        return new InkWell(
          onTap: this.onTap,
          child: new Container(
            height: 64.0,
            padding: EdgeInsets.fromLTRB(20.0, 0.0, 20.0, 10.0),
            child: new Row(
              children: <Widget>[
                new Container(width: 30.0, margin: EdgeInsets.only(right: 10), child: new Icon(icon)) ?? null,
                new Expanded(child: new Text(value, style: Theme.of(context).textTheme.subhead)),
                new Text(text, style: Theme.of(context).textTheme.subhead)
              ],
            ),
          ),
        );
      }
    

    跳转代码:

    oid onWidgetTap(WidgetPoint widgetPoint, BuildContext context) {
        List widgetDemosList = new WidgetDemoList().getDemos();//获取所有注册过的demo页面
        String targetName = widgetPoint.name;
        String targetRouter = '/category/error/404';
        widgetDemosList.forEach((item) {
          if (item.name == targetName) {
            targetRouter = item.routerName;
          }
        });
        //添加历史记录到SharedPreferences
        searchHistoryList
            .add(SearchHistory(name: targetName, targetRouter: targetRouter));
        print("searchHistoryList ${searchHistoryList.toString()}");
        Application.router.navigateTo(context, "$targetRouter");
      }
    

    这也搜索结果列表的逻辑也就出来了。

    1. TabBarView
      这个在稍后我们进行详细的分开拆解。
    2. bottomNavigationBar
    bottomNavigationBar: Material(
            color: const Color(0xFFF0EEEF), //底部导航栏主题颜色
            child: SafeArea(//safeArea
              child: Container(
                height: 65.0,
                decoration: BoxDecoration(//阴影
                  color: const Color(0xFFF0F0F0),
                  boxShadow: <BoxShadow>[
                    BoxShadow(
                      color: const Color(0xFFd0d0d0),
                      blurRadius: 3.0,
                      spreadRadius: 2.0,
                      offset: Offset(-1.0, -1.0),
                    ),
                  ],
                ),
                child: TabBar(//下面的tabbar
                    controller: controller,
                    //tab标签的下划线颜色
                    indicatorColor: Theme.of(context).primaryColor,
                    
                    // labelColor: const Color(0xFF000000),
                    indicatorWeight: 3.0,
                    //labelcolor 选中的
                    labelColor: Theme.of(context).primaryColor,
                    //labelColor: Colors.green,
                    unselectedLabelColor: const Color(0xFF8E8E8E),
                    tabs: myTabs),
              ),
            ),
          ),
    

    这里通过controllerTabBarViewTabBar关联起来进行联动。

    相关文章

      网友评论

        本文标题:Flutter Go 源码分析(二)

        本文链接:https://www.haomeiwen.com/subject/sbujxctx.html