[译] 挑战 Flutter 之 Twitter

人盡茶涼 提交于 2019-11-30 12:51:51

挑战 Flutter 将尝试在 Flutter 中重新创建特定应用的 UI 或设计。

此挑战将尝试实现安卓版 Twitter 的主页。请注意,重点将放在 UI 上,而不是实际从后端服务器获取数据。

了解应用结构

Twitter 有四个由底部导航栏控制的主要页面。

它们是:

  1. 主页(展示订阅的推文)
  2. 搜索页(搜索人员,组织等)
  3. 通知页(通知和提及)
  4. 消息页(私人消息)

BottomNavigationBar 有四个选项卡可以跳转到每个页面。

在我们的应用中将有四个不同的页面,只需点击 BottomNavigationBar 上的项目来切换页面。

建立项目

创建好 Flutter 项目(我将其命名为 twitter_ui_demo )后,清除项目中的默认代码,只留下这些:

import 'package:flutter/material.dart';  void main() => runApp(new MyApp());  class MyApp extends StatelessWidget {   //这是应用的根组件   @override   Widget build(BuildContext context) {     return new MaterialApp(       title: 'Flutter Demo',       theme: new ThemeData(         primarySwatch: Colors.blue,       ),       home: new MyHomePage(),     );   } }  class MyHomePage extends StatefulWidget {    @override   _MyHomePageState createState() => new _MyHomePageState(); }  class _MyHomePageState extends State<MyHomePage> {    @override   Widget build(BuildContext context) {     return new Scaffold(       body: new Center(       ),     );   } } 

HomePage 中有一个 Scaffold,它存有我们的 BottomNavigationBar 以及当前激活的页面。

开始

因为底部导航栏是用于导航的主要组件,所以我们先试着实现它。

这是 BottomNavigationBar 的样子:

因为没有应用中所需的图标,所以我们将使用 Font Flutter Awesome package 。在 pubspec.yaml 中添加依赖项并引入

import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 

到文件中。

BottomNavigationBar 的代码如下:

bottomNavigationBar: BottomNavigationBar(items: [   BottomNavigationBarItem(     title: Text(""),     icon: Icon(FontAwesomeIcons.home, color: selectedPageIndex == 0? Colors.blue : Colors.blueGrey,),   ),   BottomNavigationBarItem(     title: Text(""),     icon: Icon(FontAwesomeIcons.search, color: selectedPageIndex == 1? Colors.blue : Colors.blueGrey,),   ),   BottomNavigationBarItem(       title: Text(""),       icon: Icon(FontAwesomeIcons.bell, color: selectedPageIndex == 2? Colors.blue : Colors.blueGrey,)   ),   BottomNavigationBarItem(       title: Text(""),       icon: Icon(FontAwesomeIcons.envelope, color: selectedPageIndex == 3? Colors.blue : Colors.blueGrey,),   ), ], onTap: (index) {   setState(() {     selectedPageIndex = index;   }); }, currentIndex: selectedPageIndex) 

将其添加到 HomePage。

请注意,当设置图标的颜色时,我们会检查是否选中了图标,然后指定颜色。在 Twitter 中,选中的图标为蓝色,让我们将未选择的图标设置为 blueGrey。

定义一个名为 selectedPageIndex 的整型变量,用于存储所选页面的索引。在 onTap 函数中,我们将变量设置为新索引。用 setState() 包裹起来,因为我们需要刷新页面来重新渲染 AppBar。

实现的底部导航栏:

构建页面

让我们构建四个基本页面,这些页面将在单击相应的图标时显示。

建立的四个页面(在不同的文件中)如下:

用户订阅(主页)页面的代码如下:

import 'package:flutter/material.dart';  class UserFeedPage extends StatefulWidget {   @override   _UserFeedPageState createState() => _UserFeedPageState(); }  class _UserFeedPageState extends State<UserFeedPage> {   @override   Widget build(BuildContext context) {     return Container();   } } 

类似的,我们建立好搜索,通知和消息页面。

回到基础页面中,引入这些页面并定义成一个列表。

var pages = [   UserFeedPage(),   SearchPage(),   NotificationPage(),   MessagesPage(), ]; 

在 Scaffold 中,写入

body: pages[selectedPageIndex], 

它将设置 body 来展示这些页面。

到目前为止,MyHomePage 基础组件的代码如下:

class _MyHomePageState extends State<MyHomePage> {    var selectedPageIndex = 0;    var pages = [     UserFeedPage(),     SearchPage(),     NotificationPage(),     MessagesPage(),   ];    @override   Widget build(BuildContext context) {     return new Scaffold(       body: pages[selectedPageIndex],       bottomNavigationBar: BottomNavigationBar(items: [         BottomNavigationBarItem(           title: Text(""),           icon: Icon(FontAwesomeIcons.home, color: selectedPageIndex == 0? Colors.blue : Colors.blueGrey,),         ),         BottomNavigationBarItem(           title: Text(""),           icon: Icon(FontAwesomeIcons.search, color: selectedPageIndex == 1? Colors.blue : Colors.blueGrey,),         ),         BottomNavigationBarItem(             title: Text(""),             icon: Icon(FontAwesomeIcons.bell, color: selectedPageIndex == 2? Colors.blue : Colors.blueGrey,)         ),         BottomNavigationBarItem(           title: Text(""),             icon: Icon(FontAwesomeIcons.envelope, color: selectedPageIndex == 3? Colors.blue : Colors.blueGrey,),         ),       ], onTap: (index) {         setState(() {           selectedPageIndex = index;         });       }, currentIndex: selectedPageIndex,),     );   } } 

现在,我们将重新创建页面。

创建用户订阅页

页面中有两个元素:AppBar 和推文列表。

首先制作 AppBar。它有一张用户个人资料图片和一个白底黑字的标题。

appBar: AppBar(   backgroundColor: Colors.white,   title: Text("Home", style: TextStyle(color: Colors.black),),   leading: Icon(Icons.account_circle, color: Colors.grey, size: 35.0,), ), 

我们将使用图标而不是个人资料图片。

重新创建的 AppBar

现在,我们需要创建推文列表。为此,我们使用 ListView.builder()。

来看看列表项。

首先,我们需要一个由 row 和 divider 组成的 column。

在 row 中,有一个 icon 和另一个 column。

该 column 中有一个用于展示推文信息的 row,一个用于展示推文本身的 text,一个 image 和另一个用于对推文应用操作(如评论等)的 row。

为简洁起见,我们暂时抛开 image,实际上和在 row 中添加 image 一样简单。

return Column(   children: <Widget>[     Padding(       padding: const EdgeInsets.all(4.0),       child: Row(         crossAxisAlignment: CrossAxisAlignment.start,         children: <Widget>[           Padding(             padding: const EdgeInsets.all(8.0),             child: Icon(Icons.account_circle, size: 60.0, color: Colors.grey,),           ),           Expanded(             child: Column(               mainAxisAlignment: MainAxisAlignment.start,               children: <Widget>[                 Padding(                   padding: const EdgeInsets.only(top: 4.0),                   child: Row(                     mainAxisAlignment: MainAxisAlignment.spaceBetween,                     children: <Widget>[                       Expanded(                         child: Container(child: RichText(                           text: TextSpan(                             children: [                               TextSpan(text:tweet.username, style: TextStyle(fontWeight: FontWeight.w600, fontSize: 18.0, color: Colors.black),),                               TextSpan(text:" " + tweet.twitterHandle,style: TextStyle(fontSize: 16.0, color: Colors.grey)),                               TextSpan(text:" ${tweet.time}",style: TextStyle(fontSize: 16.0, color: Colors.grey))                             ]                           ),overflow: TextOverflow.ellipsis,                         )),flex: 5,                       ),                       Expanded(                         child: Padding(                           padding: const EdgeInsets.only(right: 4.0),                           child: Icon(Icons.expand_more, color: Colors.grey,),                         ),flex: 1,                       ),                     ],                   ),                 ),                 Padding(                   padding: const EdgeInsets.symmetric(vertical: 4.0),                   child: Text(tweet.tweet, style: TextStyle(fontSize: 18.0),),                 ),                 Padding(                   padding: const EdgeInsets.all(8.0),                   child: Row(                     mainAxisAlignment: MainAxisAlignment.spaceEvenly,                     children: <Widget>[                       Icon(FontAwesomeIcons.comment, color: Colors.grey,),                       Icon(FontAwesomeIcons.retweet, color: Colors.grey,),                       Icon(FontAwesomeIcons.heart, color: Colors.grey,),                       Icon(FontAwesomeIcons.shareAlt, color: Colors.grey,),                     ],                   ),                 )               ],             ),           )         ],       ),     ),     Divider(),   ], ); 

在创建一个用于提供简单推文的帮助类和一个简单的 FloatingActionButton 后,页面如下:

重新创建的 Twitter 应用

这是重新构建的 Twitter 用户订阅页。在 Flutter 中可以快速轻松地重新创建任何 UI,这说明了它的开发速度和可定制性非常不错。两者是很难兼顾的。

完整的示例托管在 Github 上。

Github 链接:https://github.com/deven98/TwitterFlutter

感谢阅读此 Flutter 挑战。可以留言告诉我任何你想要在 Flutter 中重新创建的应用。喜欢请给个 star,下次见。

不要错过:

The Medium App in Flutter

WhatsApp in Flutter

如果发现译文存在错误或其他需要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 本文永久链接 即为本文在 GitHub 上的 MarkDown 链接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!