06、FlutterMaterial Design

Material Design

Material Design(纸墨设计)是由Google推出的全新设计语言,这种设计语言为手机、平板电脑、台式机和其他平台提供更一致、更广泛的外观和感觉。

主要的Material Design风格组件如下表所示:

组件名称 中文名称 简单说明
AppBar 应用按钮组件 应用的工具按钮
AlertDialog 对话框组件 有操作按钮的对话框
BottomNavigationBar 底部导航条组件 底部导航条,可以很容易地在tap之间切换和浏览顶级视图
Card 卡片组件 带有边框阴影的卡片组件
Drawer 抽屉组件 Drawer抽屉组件可以实现类似抽屉拉开关闭的效果
FloatingActionButton 浮动按钮组件 应用的主要功能操作按钮
FlatButton 扁平按钮组件 扁平化风格的按钮
MaterialApp Material应用组件 MaterialApp代表使用纸墨设计风格的应用
PopupMenuButton 弹出菜单组件 弹出菜单按钮
Scaffold 脚手架组件 实现了基本的Material Design布局
SnackBar 轻量提示组件 一个轻量级消息提示组件,在屏幕的底部显示
SimpleDialog 简单对话框组件 简单对话框组件,只起提示作用,没有交互
TabBar 水平选项卡及视图组件 一个显示水平选项卡的Material Design组件
TextField 文本框组件 可接受应用输入文本的组件

App结构和导航组件

本节介绍的这类组件对App的结构和导航有帮助,如MaterialApp、Scaffold、AppBar、BottomNavigationBar、TabBar、Drawer等。

MaterialApp(应用组件)

MaterialApp代表使用纸墨设计风格的应用,里面包含了其所需要的基本控件。一个完整的Flutter下面就是从MaterialApp这个主组件开始的。

MaterialApp组件常见属性如下表所示:

属性名 类型 说明
title String 应用程序的标题。该标题出现在如下位置: Android:任务管理器的程序快照之上 IOS:程序切换管理器中
theme ThemeData 定义应用所使用的主题颜色,可以指定一个主题中每个控件的颜色
color Color 应用的主要颜色值,即primary color
home Widget 这个是一个Widget对象,用来定义当前应用打开时所显示的界面
routes Map<String,WidgetBuilder> 定义应用中页面跳转规则
initialRoute String 初始化路由
onGenerateRoute RouteFactory 路由回调函数。当通过Navigator.of(context).pushNamed跳转路由时,在routes查找不到时,会调用该方法。
onLocalChanged 当系统修改语言的时候,会触发这个回调
navigatorObservers List 导航观察器
debugShowMaterialGrid bool 是否显示纸墨设计基础布局网格,用来调试UI的工具
showPerformanceOverlay bool 显示性能标签

使用home属性设置应用的主页,即整个应用的主组件。示例代码如下:

import 'dart:ui';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MaterialApp示例',
      home: MyHomePage(),
    );
  }
}
// 这是一个可改变的Widget
class MyHomePage extends StatefulWidget{
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState  extends State<MyHomePage>{
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('MaterialApp示例'),
      ),
      body: Center(
        child: Text('主页', style: TextStyle(fontWeight: FontWeight.bold),),
      ),
    );
  }
}

路由处理

routes对象是一个Map<String, WidgetBuilder>。当使用Navigator.pushNamed来路由的时候,会在routes查找路由名字,然后使用对应的WidgetBuilder来构造

一个带有页面切换动画的MaterialPageRoute。如果应用只有一个界面则不需要设置该属性,使用home设置这个界面即可。

通过routes可以给MaterialApp组件初始化一个路由列表,跳转到指定页面代码如下所示:

Navigator.pushNamed(context, '/somePage');

在MaterialApp组件使用initialRoute属性可以给应用添加一个初始化路由。这两个属性代码如下:

routes:{
    '/first': (BuildContext context) => FirstPage(), // 添加路由
    '/second': (BuildContext context) => SecondPage(),
},
initialRoutes: '/first', // 初始路由页面为first页面

我们创建两个页面,在页面中添加Button,当点击按钮后互相跳转:

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Routes示例',
      home: MyHomePage(),
      routes: {
        '/first': (BuildContext context) => FirstPage(),
        '/second': (BuildContext context) => SecondPage(),
      },
      initialRoute: '/first',
    );
  }
}
// 这是一个可改变的Widget
class MyHomePage extends StatefulWidget{
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>{
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Routes示例'),),
      body: Center(
        child: Text(
          '主页',
          style: TextStyle(fontSize: 28.0),
        ),
      ),
    );
  }
}
// 第一个路由页面
class FirstPage extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('这是第一页'),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: (){
            // 路由跳转到第二个页面
            Navigator.pushNamed(context, '/second');
          },
          child: Text(
            '这是第一页',
            style: TextStyle(fontSize: 28.0),
          ),
        ),
      ),
    );
  }
}
// 第二个路由页面
class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('这是第二页'),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: (){
            // 路由跳转到第二个页面
            Navigator.pushNamed(context, '/first');
          },
          child: Text(
            '这是第二页',
            style: TextStyle(fontSize: 28.0),
          ),
        ),
      ),
    );
  }
}

自定义主题

应用程序的主题,各种定制的颜色都可以设置,用于程序主题切换。示例代码如下所示:

new MaterialApp(
    theme: ThemeData(
        // 主题色
        primarySwatch: Colors.blue,
    ),
);

Scaffold(脚手架组件)

Scaffold实现了基本的Material Design布局。只要是在Material Design中定义过的单个界面展示的布局组件元素,都可以使用Scaffold来绘制。

属性名 类型 说明
appBar AppBar 显示在界面顶部的一个AppBar
body Widget 当前界面所显示的主要内容
floatingActionButton Widget 在Material Design中定义的一个功能按钮
persistentFooterButtons List 固定在下方显示的按钮
drawer Widget 侧边栏组件
bottomNavigationBar Widget 显示在底部的导航栏按钮栏
backgroundColor Color 背景颜色
resizeToAvoidBottomPadding bool 控制界面内容body是否重新布局来避免底部被覆盖,比如当键盘显示时,重新布局避免被键盘盖住内容。默认值为true

示例代码如下所示:

import 'package:flutter/material.dart';
void main() => runApp(
      MaterialApp(
        title: 'Scaffold脚手架组件示例',
        home: LayoutDemo(),
      ),
    );
class LayoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // 头部元素 比如:左侧返回按钮 中间标题 右侧菜单
      appBar: AppBar(
        title: Text('Scaffold脚手架组件示例'),
      ),
      // 视图内容部分
      body: Center(
        child: Text("Scaffold"),
      ),
      // 底部导航栏
      bottomNavigationBar: BottomAppBar(
        child: Container(
          height: 50.0,
        ),
      ),
      // 添加FAB按钮
      floatingActionButton: FloatingActionButton(
        onPressed: () {},
        tooltip: '增加',
        child: Icon(Icons.add),
      ),
      // FAB按钮居中显示
      floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
    );
  }
}

AppBar(应用按钮组件)

应用按钮组件有AppBar和SliverAppBar。它们是纸墨设计中的AppBar,也就是Android中的Toolbar。

AppBar和SliverAppBar都继承自StatefulWidget类,都代表Toolbar,两者区别在于AppBar位置是固定在应用最上方,而SliverAppBar是可以跟随内容滚动的。

属性名 类型 默认值 说明
leading Widget null 在标题前面显示一个组件,在首页通常显示应用的logo,在其他界面通常显示为返回按钮
title Widget null Toolbar中主要内容,通常显示当前界面的标题文字
actions List null 一个Widget列表,代表Toolbar中所显示的菜单,对于常用的菜单,通常使用IconButton来表示,对于不常用的菜单通常使用Popup-MenuButton来显示为三个点,点击后弹出二级菜单
bottom PreferredSizeWidget null 通常是TabBar。用来在Toolbar标题下面显示一个Tab导航栏
elevation double 4 纸墨设计中组件z坐标顺序,对于可滚动的SliverAppBar,当SliverAppBar和内容同级的时候,该值为0,当内容滚动SliverAppBar变为Toolbar的时候,修改elevation的值
flexibleSpace Widget null 一个显示在AppBar下方的组件,高度和AppBar高度一样,可以实现一些特殊的效果,该属性通常在SliverAppBar中使用
backgroundColor Color ThemeData.primaryColor 背景色
brightness Brightness ThemeData.primaryColorBrightness AppBar的亮度,有白色和黑色两种主题
iconTheme IconThemeData ThemeData.primaryIconTheme AppBar上图标的颜色、透明度和尺寸信息。 默认值为ThemeData.primaryIconTheme
textTheme TextTheme ThemeData.primaryTextTheme AppBar上的文字样式
centerTitle bool true 标题是否居中显示,默认值根据不同的操作系统,显示方式不一样

AppBar可以显示顶部leading、title和actions等内容。底部通常为选项卡TabBar。flexibleSpace显示在AppBar的下方,高度和AppBar高度一样,可以实现一些

特殊的效果,不过该属性通常在SliverAppBar中使用。

接下来是一个示例代码,在上端左侧显示标题,右侧添加两个按钮,示例代码如下:

import 'package:flutter/material.dart';
void main() => runApp(
      MaterialApp(
        title: 'Scaffold脚手架组件示例',
        home: LayoutDemo(),
      ),
    );
class LayoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // 头部元素 比如:左侧返回按钮 中间标题 右侧菜单
      appBar: AppBar(
        title: Text('Scaffold脚手架组件示例'),
      ),
      // 视图内容部分
      body: Center(
        child: Text("Scaffold"),
      ),
      // 底部导航栏
      bottomNavigationBar: BottomAppBar(
        child: Container(
          height: 50.0,
        ),
      ),
      // 添加FAB按钮
      floatingActionButton: FloatingActionButton(
        onPressed: () {},
        tooltip: '增加',
        child: Icon(Icons.add),
      ),
      // FAB按钮居中显示
      floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
    );
  }
}

BottomNavigationBar(底部导航条组件)

BottomNavigationBar是底部导航条,可以很容易地在tap之间切换和浏览顶级视图。很多App主页底部都采用这种切换方式。

属性名 类型 说明
currentIndex int 当前索引,用来切换按钮控制
fixedColor Color 选中按钮的颜色。如果没有指定值,则用系统主题色
iconSize double 按钮图标大小
items List 底部导航条按钮集。每一项是BottomNavigationBarItem,有icon图标及title文本属性
onTap ValueChanged 按下其中某个按钮回调事件。需要根据返回的索引设置当前索引

接下来是一个示例代码,仿一个聊天软件,底部显示“信息”、“通讯录”及“发现”按钮。

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'BottomNavigatorBar示例',
      home: Scaffold(
        body: MyHomePage(),
      ),
    );
  }
}
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key}) : super(key: key);
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
  int _selectedIndex = 1; // 当前选中的索引
  final _widgetOptions = [
    Text('Index 0: 信息'),
    Text('Index 1: 通讯录'),
    Text('Index 2: 发现'),
  ];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('BottomNavigatorBar示例'),
      ),
      body: Center(
        // 居中显示某一个文本
        child: _widgetOptions.elementAt(_selectedIndex),
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(icon: Icon(Icons.chat), title: Text('信息')),
          BottomNavigationBarItem(
              icon: Icon(Icons.contacts), title: Text('通讯录')),
          BottomNavigationBarItem(
              icon: Icon(Icons.account_circle), title: Text('发现')),
        ],
        currentIndex: _selectedIndex, // 当前选中项的索引
        fixedColor: Colors.deepPurple, // 选中项的颜色
        onTap: _onItemTapped, // 选择按下处理
      ),
    );
  }
  // 选择按下处理,设置当前索引为index数
  void _onItemTapped(int value) {
    setState(() {
      _selectedIndex = value;
    });
  }
}

TabBar(水平选项卡及视图组件)

TabBar是一个显示水平选项卡的Material Design组件,通常需要配套Tab选项组件及TabBarView页面视图组件一起使用。

TabBar组件常见属性如下所示:

属性名 类型 说明
isScrollable bool 是否可以水平移动
tabs List Tab选项列表,建议不要放太多项,否则用户操作起来不方便

Tab组件常见属性如下所示:

属性名 类型 说明
icon Widget Tab图标
text String Tab文本

TabBarView组件常见属性如下所示:

属性名 类型 说明
controller TabController 指定视图的控制器
children List 视图组件的child为一个列表,一个选项卡对应一个视图

TabBar的选项卡按钮通常显示在应用页面上部,接下来用TabBar实现一个地图类软件。实现这个示例需要以下几个组件:

DefaultTabController
TabBar
Tab
TabBarView

其中,DefaultTabController这个组件是TabBar和TabBarView的控制器,是关联这两个组件的桥梁。

import 'package:flutter/material.dart';
void main() => runApp(
        DefaultTabControllerSample(),
    );
class DefaultTabControllerSample extends StatelessWidget {
  // 选项卡数据
  final List<Tab> myTabs = [
    Tab(text: '选项卡一',),
    Tab(text: '选项卡二',),
  ];
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // 用来组装TabBar及TabBarView
      home: DefaultTabController(
        length: myTabs.length,
        child: Scaffold(
          appBar: AppBar(
            // 添加导航栏
            bottom: TabBar(
              tabs: myTabs,
            ),
          ),
          // 添加导航视图
          body: TabBarView(
            children: myTabs.map((e) => Center(child: Text(e.text))).toList(),
          ),
        ),
      ),
    );
  }
}

接下来我们编写一个水平选项卡的完整示例,具体代码如下所示:

import 'package:flutter/material.dart';
void main() => runApp(TabBarSample());
class TabBarSample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // 添加DefaultTabController关联TabBar及TabBarView
      home: DefaultTabController(
        length: items.length,
        child: Scaffold(
          appBar: AppBar(
            title: Text('TabBar选项卡示例'),
            bottom: TabBar(
              isScrollable: true,
              tabs: items
                  .map((ItemView itemView) => Tab(
                        text: itemView.title,
                        icon: Icon(itemView.icon),
                      ))
                  .toList(),
            ),
          ),
          body: TabBarView(
            children: items
                .map((ItemView itemView) => Padding(
                      padding: EdgeInsets.all(16.0),
                      child: SelectedView(
                        itemView: itemView,
                      ),
                    ))
                .toList(),
          ),
        ),
      ),
    );
  }
}
// 视图项数据
class ItemView {
  String title; // 标题
  IconData icon; // 图标
  ItemView(this.title, this.icon); // 标题
}
// 选项卡数据
List<ItemView> items = [
  ItemView('自驾', Icons.directions_car),
  ItemView('自行车', Icons.directions_bike),
  ItemView('轮船', Icons.directions_boat),
  ItemView('公交车', Icons.directions_bus),
  ItemView('火车', Icons.directions_railway),
  ItemView('步行', Icons.directions_walk),
];
// 被选中的视图
class SelectedView extends StatelessWidget {
  // 视图数据
  ItemView itemView;
  SelectedView({Key key, this.itemView}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    TextStyle textStyle = Theme.of(context).textTheme.display1;
    return Card(
      color: Colors.white,
      child: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min, // 垂直方向最小化处理
          crossAxisAlignment: CrossAxisAlignment.center, // 水平方向居中对齐
          children: [
            Icon(
              itemView.icon,
              size: 128.0,
              color: textStyle.color,
            ),
            Text(
              itemView.title,
              style: textStyle,
            ),
          ],
        ),
      ),
    );
  }
}

Drawer(抽屉组件)

Drawer可以实现类似抽屉拉出推入的效果。可以从侧边栏拉出导航面板,好处是可以把一些功能菜单折叠起来。

通常Drawer是和ListView组件组合使用的,常用的属性如下所示:

属性名 类型 默认值 说明
child Widget Drawer的child可以放置任意可显示对象
elevation double 16 纸墨设计中组件的z坐标顺序

Drawer组件可以添加头部效果,用以下两个组件可以实现:

DrawerHeader:展示基本信息
UserAccountsDrawerHeader:展示用户头像、用户名、Email等信息。

DrawerHeader通常用于抽屉中在顶部展示一些基本信息,属性及描述如下:

属性名 类型 说明
decoration Decoration header区域的decoration,通常用来设置背景颜色或背景图片
curve Curve 如果decoration发生变化,则会使用curve设置的变化曲线和duration设置的动画时间来做一个切换动画
child Widget Header里面所显示的控件内容
padding EdgeInsetsGeometry Header里面内容控件的padding值,如果child为null,则这个值无效
magin EdgeInsetsGeometry Header四周的间隙

UserAccountsDrawerHeader可以设置用户头像、用户名和Email等信息,显示一个符合Material Design规范的drawer header。

属性名 类型 说明
magin EdgeInsetsGeometry Header四周的间隙
decoration Decoration header区域的decoration,通常用来设置背景颜色或背景图片
currentAccountPicture Widget 用来设置当前用户的头像
otherAccountsPictures List 用来设置当前用户其他账号的头像
accountName Widget 当前用户的名字
accountEmail Widget 当前用户的Eamil
onDetailsPressed VoidCallback 当accountName或accountEmail被点击的时候所触发的回调函数,可以用来显示其他额外信息

从UserAccountsDrawerHeader的属性和描述来看,我们可以编写一个模仿QQ侧边导航栏的效果:

import 'package:flutter/material.dart';
void main() => runApp(
  MaterialApp(
    title: 'Drawer抽屉组件示例',
    home: LayoutDemo(),
  ),
);
class LayoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Drawer抽屉组件示例'),
      ),
      drawer: Drawer(
        child: ListView(
          children: [
            // 设置用户信息 头像、用户名、Email等
            UserAccountsDrawerHeader(
              // 设置当前用户头像
              currentAccountPicture: CircleAvatar(
                backgroundImage: AssetImage('images/1.jpg'),
              ),
              accountName: Text('legend'),
              accountEmail: Text('421206860@qq.com'),
              onDetailsPressed: (){},
              otherAccountsPictures: [
                Container(
                  child: Icon(Icons.directions),
                ),
              ],
            ),
            ListTile(
              leading: CircleAvatar(child: Icon(Icons.color_lens),), // 导航栏菜单
              title: Text('个性装扮'),
            ),
            ListTile(
              leading: CircleAvatar(child: Icon(Icons.photo),),
              title: Text('我的相册'),
            ),
            ListTile(
              leading: CircleAvatar(child: Icon(Icons.wifi),),
              title: Text('免费流量特权'),
            ),
          ],
        ),
      ),
    );
  }
}

下面中使用到了图片资源需要在pubspec.yaml文件中进行配置:

flutter:
  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true
  # To add assets to your application, add an assets section, like this:
  # assets:
  #   - images/a_dot_burr.jpeg
  #   - images/a_dot_ham.jpeg
  assets:
    - images/1.jpg

按钮和提示组件

本节介绍的组件可帮助设计按钮、菜单、对话框等,包括:FloatingActionButton、FlatButton、PopupMenuButton、SimpleDialog、AlertDialog、SnackBar。

FloatingActionButton(悬停按钮组件)

FloatingActionButton对应一个圆形图标按钮,悬停在内容之上,以展示应用程序中的主要动作。通常用于Scaffold.FloatingActionButton字段。

属性名 类型 默认值 说明
child Widget child一般为Icon,不推荐使用文字
tooltip String 按钮提示文本
foregroundColor Color 前景色
backgroundColor Color 背景色
elevation double 6.0 未点击时阴影值,默认6.0
hignlightElevation double 12.0 点击时阴影值
onPressed VoidCallback 点击时间回调
shape ShapeBorder CircleBorder 定义按钮的shape,设置shape时,默认的elevation将失效

示例代码如下所示:

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FloatingActionButton示例',
      home: Scaffold(
        appBar: AppBar(
          title: Text('FloatingActionButton示例'),
        ),
        body: Center(
          child: Text(
            'FloatingActionButton示例',
            style: TextStyle(fontSize: 28.0),
          ),
        ),
        floatingActionButton: Builder(
          builder: (BuildContext context) => FloatingActionButton(
            child: Icon(Icons.add),
            tooltip: '请点击FloatingActionButton',
            // 前景色为白色
            foregroundColor: Colors.white,
            // 背景色为蓝色
            backgroundColor: Colors.blue,
            // 未点击阴影值
            elevation: 7.0,
            // 点击阴影值
            highlightElevation: 14.0,
            onPressed: (){
              // 点击回调事件,弹出一句提示语句
              Scaffold.of(context).showSnackBar(
                SnackBar(
                  content: Text('你点击了FloatingActionButton'),
                )
              );
            },
            mini: false,
            // 圆形边
            shape: CircleBorder(),
            isExtended: false,
          ),
        ),
        floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, // 居中放置
      ),
    );
  }
}

FlatButton(扁平按钮组件)

FlatButton组件是一个扁平的Material Design风格按钮,点击时会有一个阴影效果。

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FlatButton示例',
      home: Scaffold(
        appBar: AppBar(title: Text('FlatButton示例'),),
        body: Center(
          child: FlatButton(
            onPressed: (){},
            child: Text(
              'FlatButton',
              style: TextStyle(fontSize: 24.0),
            ),
          ),
        ),
      ),
    );
  }
}

PopupMenuButton(弹出菜单组件)

PopupMenuButton为弹出菜单按钮,一般放在应用页面的右上角,表示有更多操作。菜单项使用PopupMenuItem组件。

属性名 类型 说明
child Widget child如果提供则弹出菜单组件将使用此组件
icon Icon 如果提供,则弹出菜单使用此图标
itemBuilder PopupMenuItemBuilder 菜单项构造器,菜单项为任意类型,文本、图标都可以
onSelected PopupMenuItemSelect 当某项菜单被选中时回调的方法

接下来我们编写一个视频会议产品中关于会控的菜单。包括添加成员、锁定会议、修改布局、挂断所有。

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
// 会控菜单项
enum ConferenceItem{addMember, LockConference, ModifyLayout, TurnoffAll}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'PopupMenuButton组件示例',
      home: Scaffold(
        appBar: AppBar(title: Text('PopupMenuButton组件示例'),),
        body: Center(
          child: FlatButton(
            onPressed: (){},
            child: PopupMenuButton<ConferenceItem>(
              onSelected: (ConferenceItem item){},
              // 菜单构造器
              itemBuilder: (BuildContext context) => <PopupMenuEntry<ConferenceItem>>[
                // 菜单项
                PopupMenuItem(
                  value: ConferenceItem.addMember,
                  child: Text('添加成员'),
                ),
                PopupMenuItem(
                  value: ConferenceItem.LockConference,
                  child: Text('锁定会议'),
                ),
                PopupMenuItem(
                  value: ConferenceItem.ModifyLayout,
                  child: Text('修改布局'),
                ),
                PopupMenuItem(
                  value: ConferenceItem.TurnoffAll,
                  child: Text('挂断所有'),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

SimpleDialog(简单对话框组件)

SimpleDialog组件用于设计简单对话框,可以显示附加的提示或操作,组件的属性如下表所示:

属性名 类型 说明
children List 对话框子项,典型的应用是一个列表
title Widget 通常是一个文本组件
contentPadding EdgeInsetsGeometry 内容部分间距大小
titlePadding EdgeInsetsGeometry 标题部分间距大小

简单对话框通常需要配合SimpleDialogOption组件一起使用,接下来看示例:

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
// 会控菜单项
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'SimpleDialog组件示例',
      home: Scaffold(
        appBar: AppBar(title: Text('SimpleDialog组件示例'),),
        body: Center(
          child: SimpleDialog(
            title: Text('对话框标题'),
            children: [
              SimpleDialogOption(
                onPressed: (){},
                child: Text('第一行信息'),
              ),
              SimpleDialogOption(
                onPressed: (){},
                child: Text('第二行信息'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

注意:对话框都是某个动作来触发渲染的,一般要封装在一个方法里实现。这个过程是异步的需要加入async/await处理。

AlertDialog(提示对话框组件)

AlertDialog组件比SimpleDialog对话框复杂一些,不仅有提示内容,还有操作按钮等。内容部分可以用SingleChildScrollView进行包裹。

属性名 类型 说明
actions List 对话框底部操作按钮,如确定、取消等
title Widget 通常是一个文本组件
contentPadding EdgeInsetsGeometry 内容部分间距大小
content Widget 内容部分,通常为对话框的提示内容
titlePadding EdgeInsetsGeometry 标题部分间距大小

下面编写一个删除确认的示例:

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
// 会控菜单项
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'AlertDialog示例',
      home: Scaffold(
        appBar: AppBar(title: Text('AlertDialog示例'),),
        body: Center(
          child: AlertDialog(
            // 对话框标题
            title: Text('提示'),
            content: SingleChildScrollView(
              // 对话框内容部分
              child: ListBody(
                children: [
                  Text('是否要删除?'),
                  Text('一旦删除数据不可恢复')
                ],
              ),
            ),
            actions: [
              FlatButton(
                child: Text('确定'),
                onPressed: (){},
              ),
              FlatButton(
                child: Text('取消'),
                onPressed: (){},
              ),
            ],
          ),
        ),
      ),
    );
  }
}

SnackBar(轻量提示组件)

SnackBar是一个轻量级消息提示组件,在屏幕底部显示,主要的属性如下所示:

属性名 类型 默认值 说明
action SnackBarAction 提示消息里执行的动作,比如用户想撤销时可以点击操作
animation Animation 给组件添加动画效果
content Widget 提示消息内容,通常为文本组件
duration Duration 4.0秒 动画执行的时长
backgroundColor Color 消息面板的背景色

完整的示例代码如下:

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'SnackBar示例',
      home: Scaffold(
        appBar: AppBar(title: Text('SnackBar示例'),),
        body: Center(
          child: Text('SnackBar示例', style: TextStyle(fontSize: 28.0),),
        ),
        floatingActionButton: Builder(builder: (BuildContext context) => FloatingActionButton(
          child: Icon(Icons.add),
          onPressed: (){
            // 点击回调事件,弹出提示语句
            Scaffold.of(context).showSnackBar(SnackBar(content: Text('显示SnackBar'),));
          },
        ),),
        floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
      ),
    );
  }
}

其他组件

本节介绍的组件包括文本框、卡片等内容的设计。如TextField、Card等。

TextField(文本框组件)

TextField组件就是用来做文本输入的组件,常见的属性如下表所示:

属性名 类型 说明
maxLength int 最大长度
maxLines int 最大行数
autocorrect bool 是否自动更正
autofocus bool 是否自动对焦
obscureText bool 是否是密码
textAlign TextAlign 文本对齐方式
style TextStyle 输入文本的样式
inputFormatters List 允许的输入格式
onChanged ValueChanged 内容改变的回调
onSubmitted ValueChanged 内容提交时回调
enable bool 是否禁用

获取文本内容仅有输入还不行,还需要传递Controller给TextField。用来监听文本内容的变化。初始化监听器代码如下:

final TextEditingController controller = TextEditingController();
controller.addListener(){
};

绑定监听器代码如下所示:

child:TextField(
    controller: controller
),

TextField完整示例代码如下所示:

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // 添加文本编辑控制器 监听文本输入内容变化
    TextEditingController controller = new TextEditingController();
    controller.addListener(() {
      print('输入的内容为:${controller.text}');
    });
    return MaterialApp(
      title: 'TextField示例',
      home: Scaffold(
        appBar: AppBar(title: Text('TextField示例'),),
        body: Center(
          child: Padding(
            padding: EdgeInsets.all(20.0),
            child: TextField(
              // 绑定controller
              controller: controller,
              // 最大长度,设置此项会在右下角有一个输入数量的统计字符串
              maxLength: 30,
              // 最大行数
              maxLines: 1,
              // 是否自动更正
              autocorrect: true,
              // 是否自动对焦
              autofocus: true,
              // 文本对其方式
              textAlign: TextAlign.center,
              // 输入文本的样式
              style: TextStyle(fontSize: 26.0, color: Colors.green),
              // 文本内容改变时回调
              onChanged: (text){
                print('文本内容改变时回调:$text');
              },
              // 内容提交时回调
              onSubmitted: (text){
                print('内容提交时回调: $text');
              },
              // 是否禁用
              enabled: true,
              // 添加装饰效果
              decoration: InputDecoration(
                fillColor: Colors.grey.shade200,
                filled: true,
                helperText: '用户名',
                // 左侧图标
                prefixIcon: Icon(Icons.person),
                // 右侧文本提示
                suffixText: '用户名',
                hintText: '请输入用户名'
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Card(卡片组件)

Card即卡片组件块,内容可以由大多数类型的Widget构成,通常和ListTile一起使用。

属性名 类型 默认值 说明
child Widget 组件的子元素,任意Widget都可以
magin EdgeInsetsGeometry 围绕在decoration和child之外的空白区域,不属于内容区域
shape ShapeBorder RoundedRectangBorder Card的阴影效果,默认的阴影效果为圆角的长方形边,弧度为4.0

完整的示例代码如下:

import 'package:flutter/material.dart';
void main() => runApp(
  MaterialApp(
    title: 'Card布局示例',
    home: MyApp(),
  ),
);
class MyApp extends StatelessWidget{
  var card = new SizedBox(
    height: 250.0,
    child: Column(
      children: [
        ListTile(
          title: Text('11111111111111111', style: TextStyle(fontWeight: FontWeight.w300),),
          subtitle: Text('222222222222'),
          leading: Icon(Icons.home, color: Colors.lightBlue,),
        ),
        Divider(),
        ListTile(
          title: Text('aaaaaaaaaaaaaaa', style: TextStyle(fontWeight: FontWeight.w300),),
          subtitle: Text('bbbbbbbbbbbb'),
          leading: Icon(Icons.school, color: Colors.lightBlue,),
        ),
        Divider(),
      ],
    ),
  );
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Card布局示例'),),
      body: Center(
        child: card,
      ),
    );
  }
}
原文地址:https://www.cnblogs.com/pengjingya/p/14928524.html