阿里 Flutter-go 项目拆解笔记(八)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ITxiaodong/article/details/88702597

Flutter-go 项目地址是:https://github.com/alibaba/flutter-go

上文 我们分析了 第四个 Tab 页面,主要分析了 翻页动画的实现

这篇文章主要拆解 详情页面和页面跳转

下图是整理后的 详情页面和页面跳转 有关的内容:
功能拆解.png

ps: 这篇代码量有点多,看不懂代码可以先知道这个功能是如何去实现的,待后边动手实现项目的时候回继续补充讲解。

页面跳转

页面跳转使用的是fluro库,fluro库的使用只需以下两步即可。

  1. 先做实例化final router = Router();
  2. 定义handler,用于处理路由的路径和参数的传递,如:
var usersHandler = Handler(handlerFunc: (BuildContext context, Map<String, dynamic> params) {
  // UsersScreen 页面,接收参数为 id 的值
  return UsersScreen(params["id"][0]);
});

void defineRoutes(Router router) {
  //  下面的路由会拦截如: /users/1234,的页面路径
  router.define("/users/:id", handler: usersHandler);

  // 需要页面跳转动画可以使用 transitionType
  // router.define("users/:id", handler: usersHandler, transitionType: TransitionType.inFromLeft);
}

下面来看一下如何使用Router去进入Widget详情页面。

  1. main.dart中实例化Router,然后将对象赋值给application.dart中的Router对象,下次使用直接Application.router即可调用
  2. router_handler.dart文件中声明了handler,然后在routers.dart文件中调用对应的handler。如:
// 定义Handler
var categoryHandler = new Handler(
  handlerFunc: (BuildContext context, Map<String, List<String>> params) {
    // 获取参数
    String name = params["type"]?.first;

    return new CategoryHome(name);
  },
);

// 定义路径
router.define('/category/:type', handler: categoryHandler);

// 页面跳转
 Application.router.navigateTo(
      context, "/category/${item.name}", 
      transition: TransitionType.inFromRight);

详情页面分析

分类页面

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      // 当前页面将退出,为了多层分类页面的复用
      body: WillPopScope(
        onWillPop: () {
          return back();
        },
        child: ListView(
          children: <Widget>[
            _buildContent(),
          ],
        ),
        // child: Container(color: Colors.blue,child: Text('123'),),
      ),
    );

网格布局主要在 _buildContentWidgetItemContainer控件中通过循环去实现。WidgetItemContainer实现的思路如下:
image.png
伪代码如下:

 List<Widget> _buildColumns(context) {
    List<Widget> _listWidget = [];
    List<Widget> _listRows = [];
    int addI;
    for (int i = 0, length = categories.length; i < length; i += columnCount) {
      _listRows = [];
      for (int innerI = 0; innerI < columnCount; innerI++) {
        addI = innerI + i;
        if (addI < length) {
          dynamic item = categories[addI];
          _listRows.add(
            Expanded(
              flex: 1,
              child: WidgetItem(
                title: item.name,
                onTap: () {
                  if (isWidgetPoint) {
                    String targetName = item.name;
                    String targetRouter = '/category/error/404';
                    widgetDemosList.forEach((item) {
                      if (item.name == targetName) {
                        targetRouter = item.routerName;
                      }
                    });
                    Application.router.navigateTo(context, "$targetRouter", transition: TransitionType.inFromRight);
                  } else {
                    Application.router
                        .navigateTo(context, "/category/${item.name}", transition: TransitionType.inFromRight);
                  }
                },
                index: addI,
                totalCount: length,
                rowLength: columnCount,
                textSize: isWidgetPoint ? 'middle' : 'small',
              ),
            ),
          );
        } else {
          _listRows.add(
            Expanded(
              flex: 1,
              child: Container(),
            ),
          );
        }
      }
      _listWidget.add(
        Row(
          children: _listRows,
        ),
      );
    }
    return _listWidget;
  }

Widget 详情页面

路径:lib/componets/widget_demos.dart

return Scaffold(
        key: _scaffoldKey,
        appBar: AppBar(
          title: Text(widget.title),
          actions: <Widget>[
            new IconButton(
              tooltip: 'goBack home',
              onPressed: () {
                Navigator.popUntil(context, ModalRoute.withName('/'));
              },
              icon: Icon(Icons.home),
            ),
            new IconButton(
              tooltip: 'collection',
              onPressed: _getCollection,
              icon: Icon(_collectionIcons),
            ),
            PopupMenuButton<String>(
              onSelected: _selectValue,
              itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
                    const PopupMenuItem<String>(
                      value: 'doc',
                      child: ListTile(
                        leading: Icon(
                          Icons.library_books,
                          size: 22.0,
                        ),
                        title: Text('查看文档'),
                      ),
                    ),
                    const PopupMenuDivider(),
                    const PopupMenuItem<String>(
                      value: 'code',
                      child: ListTile(
                        leading: Icon(
                          Icons.code,
                          size: 22.0,
                        ),
                        title: Text('查看Demo'),
                      ),
                    ),
                  ],
            ),
          ],
        ),
        body: Container(
          padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0),
          child: ListView(
            shrinkWrap: true,
            padding: const EdgeInsets.all(0.0),
            children: <Widget>[
              Column(
                children: _buildContent(),
              ),
            ],
          ),
        ),
        bottomNavigationBar:
            (widget.bottomNaviBar is Widget) ? widget.bottomNaviBar : null);
  }

从上面的源码可以看出 点击图标回首页 的操作是:

Navigator.popUntil(context, ModalRoute.withName('/'));

PopupMenu 弹窗的实现也是很简单,直接使用PopupMenuButton即可生成。

中间的控件描述是通过获取具体的Widget去展示的,在展示的时候用到了Markdownflutter_markdown

例如:展示Appbar组件的介绍,在widgets/components/Bar/AppBar路径下有个index.dart,在index.dart里面使用markdown格式返回了WidgetDemo控件要显示的内容(也就是展示的详情页)

那么那么多个widget它是如何去区分点击的是那个呢?

void onWidgetTap(WidgetPoint widgetPoint) {
    String targetName = widgetPoint.name;
    String targetRouter = '/category/error/404';
    widgetDemosList.forEach((item) {
      // print("targetRouter = item.routerName> ${[item.name,targetName]}");
      if (item.name == targetName) {
        targetRouter = item.routerName;
      }
    });
    Application.router.navigateTo(context, "$targetRouter");
  }

通过点击事件可以看出,页面跳转的时候传递了widget的名称,所以它是通过名称来区别widget的,在widget_demo.dart中通过便利所有widget找到该名称的widget然后用MarkDownBody去展示。伪代码如下:

// 传递 title
class _DemoState extends State<Demo> {
  @override
  Widget build(BuildContext context) {
    return WidgetDemo(
      title: 'AppBar',
      codeUrl: 'components/Bar/AppBar/demo.dart',
      contentList: allDomes(context, this),
      docUrl: 'https://docs.flutter.io/flutter/material/AppBar-class.html',
    );
  }
}
// 得到 title,遍历获取该 widget 并展示
List<Widget> _buildContent() {
    List<Widget> _list = [
      SizedBox(
        height: 10.0,
      ),
    ];
    widget.contentList.forEach((item) {
      if (item.runtimeType == String) {
        _list.add(MarkdownBody(item));
        _list.add(
          SizedBox(
            height: 20.0,
          ),
        );
      } else {
        _list.add(item);
      }
    });
    return _list;
  }

为了适配底部导航栏Widget还添加了bottomNavigationBar去控制显示导航栏。

web 页面

接收urltitle参数 ,使用了WebviewScaffold控件,这是一个第三方的库(flutter_webview_plugin)提供的控件,该库使用文档

 return Scaffold(
       key: _scaffoldKey,
      appBar: AppBar(
        title: Text(widget.title),
        actions: <Widget>[
          new IconButton(
            tooltip: 'goBack home',
            onPressed: _getCollection,
            icon: Icon(
              _collectionIcons,
            ),
          ),
        ],
      ),
      body: WebviewScaffold(
        url: widget.url,
        withZoom: false,
        withLocalStorage: true,
        withJavascript: true,
      ),
    );

调用本地浏览器打开网页

使用了url_launcher库,该库允许您打开移动平台上的默认浏览器来显示给定的URL,它在AndroidiOS上均受支持。

// 点击首页的 Banner 启动手机浏览器
  void _launchURL(String url) async {
    if (await canLaunch(url)) {
      await launch(url);
    } else {
      throw 'Could not launch $url';
    }
  }
// 调用
    if (arr.length > 0) {
      list.add(HomeBanner(bannerStories, (story) {
        _launchURL('${story.url}');
      }));
    }

代码阅读页面

使用了RichText控件,同时使用了代码高亮工具类SyntaxHighlighterStyle去高亮代码。

 Widget build(BuildContext context) {
    // 定义好样式
    final SyntaxHighlighterStyle style =
        Theme.of(context).brightness == Brightness.dark
            ? SyntaxHighlighterStyle.darkThemeStyle()
            : SyntaxHighlighterStyle.lightThemeStyle();

    Widget body;
    if (_exampleCode == null) {
      body = const Center(child: CircularProgressIndicator());
    } else {
      Widget _codeWidget;
      try{
       // 使用代码高亮
        DartSyntaxHighlighter(style).format(_exampleCode);
      // 富文本控件展示
        _codeWidget = RichText(
          text: TextSpan(
                style: const TextStyle(fontFamily: 'monospace', fontSize: 10.0),
                children: <TextSpan>[
                  DartSyntaxHighlighter(style).format(_exampleCode)
                ],),
        );
      }catch (err){
        _codeWidget = Text(_exampleCode);
      }
      body = SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: _codeWidget,
        ),
      );
    }

点击按钮 Tab 页面切换

 /// 第四个页面点击 回到首页 按钮
  _goHomePage(context) {
    Navigator.of(context)
        .pushNamedAndRemoveUntil('/home', (Route<dynamic> route) => false);
  }

本篇完~

猜你喜欢

转载自blog.csdn.net/ITxiaodong/article/details/88702597