Flutter Material Design风格组件

1. 概述

Material Design是由谷歌推出的全新设计语言,这种设计语言旨在为手机、平板电脑、台式机和其他平台提供更一致、更广泛的外观和感觉。Material Design风格是一直非常有质感的设计风格,并会提供一些默认的交互动画。

2. App结构和导航组件

2.1 MaterialApp(应用组件)

MaterialApp代表使用Material Design风格的应用,里面包含了其他所需的基本控件。官方提供的示例demo就是从MaterialApp这个主组件开始的。

MaterialApp组件常见属性

属性名类型说明
titleString应用程序的标题。该标题出现在以下位置:Android:任务管理器的程序快照上; IOS:程序切换管理器中
themeThemeData定义应用所使用的主题颜色,可以指定主题中每个控件的颜色
colorColor应用的主要颜色值,即primary color
homeWidget用来定义当前应用打开时所显示的界面
routesMap<String, WidgetBuilder>定义应用中页面跳转规则
initialRouteString初始化路由
onGenerateRouteRouteFactory路由回调函数。当通过Navigator.of(context).pushNamed跳转路由的时候,在routes查找不到时,会调用该方法
onLocaleChanged-当系统修改语言的时候,会触发这个回调
navigatorObserversList<NavigatorObserver>导航观察器
debugShowMaterialGridbool是否显示布局网格,用来调试UI的工具
showPerformanceOverlaybool显示性能标签

2.1.1 设置主页

使用home属性设置应用的主页,即整个应用的主组件。

2.1.2 路由处理

routes对象是一个Map<String, WidgetBuilder>。当使用Navigator.pushNamed来跳转路由的时候,通过routes查找路由名字,然后使用对应的WidgetBuilder来构造一个带有页面切换动画的MaterialPageRoute。如果应用只有一个界面,则不用设置整个属性,使用home即可。

2.1.3 自定义主题

应用程序的主题,各种定制的颜色都可以设置,用于程序主题切换。

2.2 Scaffold(脚手架组件)

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

常用属性

属性名类型说明
appBarAppBar显示在界面顶部的一个AppBar
bodyWidget当前界面所显示的主要内容
floatingActionButtonWidget在Material Design中定义的一个功能按钮
persistentFooterButtonsList< Widget>固定在下方显示的按钮
drawerWidget侧边栏组件
bottomNavigatorBarWidget显示在底部的导航栏
backgroudColorColor背景颜色
resizeToAvoidBottomPaddingbool控制界面内容body是否重新布局来避免底部被覆盖,比如当键盘显示时,重新布局避免键盘盖住内容。默认为true

2.3 AppBar(应用按钮组件)

应用按钮组件有AppBar和SliverAppBar。它们是Material Design中的AppBar,也就是Android中的ToolBar。
AppBar和SliverAppBar都继承自StatefulWidget,两者的区别在于AppBar的位置是固定在应用最上面的;而SliverAppBar是可以跟随内容滚动的。

常用属性

属性名类型默认值说明
leadingWidgetnull在标题前面显示一个组件,在首页通常显示应用的logo;其他界面通常显示返回按钮
titleWidgetnull当前界面的标题
actionsList<Widget>null一个Widget列表,常用菜单通常使用IconButton,不常用的通常使用PopupMenuButton显示三个点,点击后弹出二级菜单
bottomPreferredSizeWidgetnull通常是一个TabBar。用来在标题下方显示一个Tab导航栏
elevationdouble4控制AppBar下方阴影的高度
flexibleSpaceWidgetnull一个显示在AppBar下方的组件,高度和AppBar一样,可以实现一些特殊的效果,该属性通常在SliverAppBar中使用
backgroundColorColorThemeData.primaryColor背景色
brightnessBrightnessThemeData.primaryColorBrightnessAppBar的亮度,有白色和黑色两种主题
iconThemeIconThemeDataThemeData.primaryIconThemeAppBar上图标的颜色、透明度和尺寸信息
textThemeTextThemeThemeData.primaryTextThemeAppBar上文字样式
centerTitlebool-标题是否居中显示,不同操作系统,显示方式不一样

2.4 BottomNavigatorBar(底部导航栏)

BottomNavigatorBar是底部导航栏,可以很容易地在tab之间切换和浏览顶级视图。

常见属性

属性名类型说明
currentIndexint当前索引
fixedColorColor选中按钮的颜色。不指定则使用系统主题色
iconSizedouble按钮图形大小
itemsList<BottomNavigatorBarItem>底部导航栏按钮集。每一项是一个BottomNavigatorBarItem,包含icon图标和title文本
onTapValueChanged<int>按下按钮的回调事件。需要根据返回的索引设置当前索引

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

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

属性名类型说明
isScrolledbool是否可以水平滚动
tabsList<Widget>Tab选项列表

Tab常见属性

属性名类型说明
iconWidgetTab图标
textStringTab文本

TabBarView常见属性

属性名类型说明
controllerTabController指定视图的控制器
childrensList<Widget>视图组件的child为一个列表,一个选项卡对应一个视图

2.6 Drawer(抽屉组件)

Drawer可以实现类似抽屉拉入推出的效果,通常与ListView组合使用。

Drawer常用属性

属性名类型默认值说明
childWidget-Drawer的child可以放置任意可显示的组件
elevationdouble16阴影尺寸

Drawer可以添加头部效果:

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

DrawerHeader常用属性

属性名类型说明
decorationDecorationheader区域的decoration,通常用来设置背景颜色或背景图片
curveCurve如果decoration发生了变化,则会使用curve设置的变化曲线和duration设置的动画时间来做一个动画效果
childWidgetHeader里面所显示的内容控件
paddingEdgeInsetsGeometryHeader里面内容控件的padding值,如果child为null,该值无效
marginEdgeInsetsGeometryHeader四周的间隙

UserAccountsDrawerHeader常用属性

属性名类型说明
marginEdgeInsetsGeometryHeader四周的间隙
decorationDecorationheader区域的decoration,通常用来设置背景颜色或背景图片
currentAccountPictureWidget用来设置当前用户的头像
otherAccountsPictureWidget用来设置当前用户其他账号的头像
accountNameWidget当前用户的名字
accountEmailWidget当前用户的Email
onDetailsPressedVoidCallback当accountName或者accountEmail被点击的时候所触发的回调函数,可以用来显示其他额外的信息

3. 按钮和提示组件

3.1 FloatingActionButton(悬停按钮组件)

FloatingActionButton对应一个圆形图标按钮,悬停在内容之上,以展示应用程序中的主要动作。类似IOS里的小白点。

FloatingActionButton常用属性

属性名类型默认值说明
child---
tooltip---
foregroundColor---
backgroundColor---
elevation---
highlightElevation---
onPressed---
shape---

3.2 FlatButton(扁平按钮组件)

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

3.3 PopupMenuButton(弹出菜单组件)

PopupMenuButton一般放在应用右上角,表示更多操作,菜单项使用PopupMenuItem组件。

PopupMenuButton常用属性

属性名类型说明
child--
icon--
itemBuilder--
onSelected--

3.4 SimpleDialog(简单对话框组件)

SimpleDialog用于设计简单的对话框,可以显示附加的提示或操作。SimpleDialog通常需要配合SimpleDialogOption组件一起使用。

SimpleDialog常用属性

属性名类型说明
childrenList<Widget>对话框子项
titleWidget通常是一个文本控件
contentPaddingEdgeInsetsGeometry内容部分间距
titlePaddingEdgeInsetsGeometry标题部分间距
  • 通常对话框都是由某个动作来触发渲染的,比如点击按钮,点击菜单等。所以对话框一般要封装在一个方法里实现。
  • 另外这个过程是异步的需要加入async/await处理。

3.5 AlertDialog(提示对话框组件)

AlertDialog比SimpleDialog对话框又复杂一些,不仅有提示内容,还有一些操作按钮,如确定和取消等,内容部分可以用SingleChildScrollView进行包裹。

AlertDialog常用属性

属性名类型说明
actionsList<Widget>对话框底部操作按钮,如确定、取消等
titleWidget通常是一个文本控件
contentPaddingEdgeInsetsGeometry内容部分间距
contentWidget内容部分,通常为对话框的提示内容
titlePaddingEdgeInsetsGeometry标题部分间距

3.6 SnackBar(轻量提示组件)

SnackBar是一个轻量级消息提示组件,在屏幕底部显示。

SnackBar常用属性

属性名类型默认值说明
actionSnackBarAction-提示消息里执行的动作,比如用户想撤销时可以点击操作
animationAnimation<double>-给组件添加动画效果
contentWidget-提示消息内容,通常为文本组件
durationDuration4.0秒动画执行的时长
backgroundColorColor-消息面板的背景色

4. 其他组件

4.1 TextField(文本框组件)

TextField就是用来做文本输入的组件。

  • 注意与Text组件区分,Text主要用于显示文本,不接受文本输入。

TextField常用属性

属性名类型说明
maxLengthint最大长度
maxLinesint最大行数
autocorrectbool是否自动更正
autofocusbool是否自动对焦
obscureTextbool是否是密码
textAlignTextAlign文本对齐方式
styleTextStyle文本的样式
inputFormattersList<TextInputFormatter>允许的输入格式
onChangedVoidChanged<String>内容改变时回调
onSubmittedVoidChanged<String>内容提交时回调
enabledbool是否禁用

4.2 Card(卡片组件)

Card内容可以由大多数类型的组件构成,但通常与ListTitle一起使用。Card有一个child,但可以是支持多个child的Row、Column、ListView、GridView或其他小部件。默认情况下,Card将其大小缩小为0像素。可以使用SizedBox来限制Card的大小。

Card常用属性

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

5. 示例1

import 'dart:math';

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MaterialApp',
      routes: {'/other': (BuildContext context) => OtherPage()},
//      initialRoute: '/other',
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int _currentIndex = 0;
  final _widgetOptions = [
    Text('信息'),
    Text('通讯录'),
    Text('发现'),
    Text('我'),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('MaterialApp示例'),
//        centerTitle: true,//是否居中
        leading: Icon(Icons.star), //左侧图标
        elevation: 0.0, //阴影
        actions: <Widget>[
          Icon(Icons.search),
          Icon(Icons.more_vert),
        ],
      ),
      body: Center(child: _widgetOptions.elementAt(_currentIndex)),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.pushNamed(context, '/other');
        },
        tooltip: '路由跳转',
        foregroundColor: Color(0xffffffff),
        backgroundColor: Color(0xff000000),
        //阴影
        elevation: 0.0,
        child: Icon(Icons.arrow_forward),
//        shape: RoundedRectangleBorder(),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
      bottomNavigationBar: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(
            icon: Icon(Icons.message),
            title: Text('信息'),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.contacts),
            title: Text('通讯录'),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.near_me),
            title: Text('发现'),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.account_circle),
            title: Text('我'),
          ),
        ],
        currentIndex: _currentIndex,
        type: BottomNavigationBarType.fixed,
        onTap: (index) {
          setState(() {
            _currentIndex = index;
          });
        },
      ),
      drawer: _buildDrawer(),
    );
  }

  Drawer _buildDrawer() {
    return Drawer(
//        elevation: 0.0,
      child: ListView(
        children: <Widget>[
          UserAccountsDrawerHeader(
            currentAccountPicture: CircleAvatar(
              backgroundImage: NetworkImage('https://randomuser.me/api/portraits/lego/2.jpg'),
            ),
            accountName: Text('傅红雪',style: TextStyle(fontSize:18.0),),
            accountEmail: Text('345166018@qq.com',style: TextStyle(fontSize:16.0)),
            otherAccountsPictures: <Widget>[
              Icon(Icons.camera_alt),
            ],
            decoration: BoxDecoration(
              color: Colors.deepOrange,
//              image: DecorationImage(
//                image: AssetImage('assets/images/xxx.jpg'),
//                fit: BoxFit.fill,
//              ),
            ),
          ),
          ListTile(
            leading: Icon(Icons.payment),
            title: Text('会员特权'),
          ),
          ListTile(
            leading: Icon(Icons.payment),
            title: Text('会员特权'),
          ),
          ListTile(
            leading: Icon(Icons.payment),
            title: Text('会员特权'),
          ),
          AboutListTile(
            icon: Icon(Icons.error),
            child: Text('关于'),
            applicationName: '通讯录',
            applicationVersion: '1.0',
          ),
        ],
      ),
    );
  }
}

class OtherPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('OtherPage'),
      ),
    );
  }
}

效果显示:
在这里插入图片描述
在这里插入图片描述

6. 示例2-Dialog

import 'dart:math';

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MaterialApp',
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            SimpleDialog(
              title: Text('对话框标题'),
              children: <Widget>[
                SimpleDialogOption(
                  child: Text('选项1'),
                  onPressed: () {
                    print('选项1');
                  },
                ),
                SimpleDialogOption(
                  child: Text('选项2'),
                  onPressed: () {
                    print('选项2');
                  },
                )
              ],
            ),
            RaisedButton(
              child: Text('删除'),
              onPressed: () {
                showDialog(
                    context: context,
                    builder: (BuildContext context) {
                      return AlertDialog(
                        title: Text('提示'),
                        content: SingleChildScrollView(
                          child: ListBody(
                            children: <Widget>[
                              Text('是否删除?'),
                              Text('删除后不可恢复!'),
                            ],
                          ),
                        ),
                        actions: <Widget>[
                          FlatButton(
                            child: Text('确定'),
                            onPressed: () {
                              Navigator.of(context).pop();
                            },
                          ),
                          FlatButton(
                            child: Text('取消'),
                            onPressed: () {
                              Navigator.of(context).pop();
                            },
                          ),
                        ],
                      );
                    });
              },
            ),
          ],
        ),
      ),
    );
  }
}

效果显示:
在这里插入图片描述
在这里插入图片描述


7. 示例3-Card(卡片组件)

import 'dart:math';

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MaterialApp',
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Card示例'),
        leading: Icon(Icons.payment),
      ),
      body: ListView(
        children: <Widget>[
          MyCardWidget(
            title: '小明',
            subTitle: '棒棒的',
            icon: Icons.home,
            favoriteCount: 66,
            iconBgColor: Colors.blue,
            imageAssetPath: 'assets/images/pic.jpg',
          ),
          MyCardWidget(
            title: '小明',
            subTitle: '真棒',
            icon: Icons.home,
            favoriteCount: 77,
            iconBgColor: Colors.orange,
            imageAssetPath: 'assets/images/pic.jpg',
          ),
          MyCardWidget(
            title: '小明',
            subTitle: '很棒',
            icon: Icons.home,
            favoriteCount: 89,
            iconBgColor: Colors.red,
            imageAssetPath: 'assets/images/pic.jpg',
          ),
//          buildCard(),
//          buildCard(),
//          buildCard(),
        ],
      ),
    );
  }

  Widget buildCard() {
    return Container(
      decoration: BoxDecoration(
        borderRadius: BorderRadius.all(Radius.circular(15.0)),
      ),
      child: Card(
        child: Column(
          children: <Widget>[
            Container(
              child: Image.asset(
                'assets/images/pic.jpg',
                width: double.infinity,
                height: 150.0,
                fit: BoxFit.cover,
              ),
            ),
            Row(
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.all(15.0),
                  child: Container(
                    child: Icon(Icons.home),
                  ),
                ),
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      Text(
                        '棒棒的',
                        style: TextStyle(fontSize: 22.0),
                      ),
                      Text(
                        '500',
                        style: TextStyle(fontSize: 14.0),
                      ),
                    ],
                  ),
                ),
                Container(
                  child: Column(
                    children: <Widget>[
                      Icon(
                        Icons.favorite,
                        color: Color(0xffff0000),
                      ),
                      Text('66'),
                    ],
                  ),
                ),
              ],
            )
          ],
        ),
      ),
    );
  }
}

class MyCardWidget extends StatelessWidget {
  final String imageAssetPath;
  final IconData icon;
  final Color iconBgColor;
  final String title;
  final String subTitle;
  final int favoriteCount;

  const MyCardWidget(
      {Key key,
      this.imageAssetPath,
      this.icon,
      this.iconBgColor,
      this.title,
      this.subTitle,
      this.favoriteCount})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10.0),
      child: Card(
//        shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20.0))),
        elevation: 10.0,
        child: Column(
          children: <Widget>[
            Image.asset(
              imageAssetPath,
              width: double.infinity,
              height: 150.0,
              fit: BoxFit.cover,
            ),
            Row(
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.all(15.0),
                  child: Container(
                    padding: const EdgeInsets.all(10.0),
                    decoration: BoxDecoration(
                      color: iconBgColor,
                      borderRadius:
                          BorderRadius.all(const Radius.circular(15.0)),
                    ),
                    child: Icon(
                      icon,
                      color: Colors.white,
                    ),
                  ),
                ),
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      Text(
                        title,
                        style: TextStyle(
                          fontSize: 25.0,
                        ),
                      ),
                      Text(
                        subTitle,
                        style: TextStyle(
                          fontSize: 16.0,
                          color: Colors.grey,
                        ),
                      ),
                    ],
                  ),
                ),
                Container(
                  width: 2.0,
                  height: 70.0,
                  decoration: BoxDecoration(
                    gradient: LinearGradient(
                      colors: [
                        Colors.white,
                        Colors.white,
                        Colors.grey,
                      ],
                      begin: Alignment.topCenter,
                      end: Alignment.bottomCenter,
                    ),
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.only(left: 15.0, right: 15.0),
                  child: Column(
                    children: <Widget>[
                      Icon(
                        Icons.favorite_border,
                        color: Colors.red,
                      ),
                      Text(
                        '$favoriteCount',
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}


效果显示:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值