前言
上篇关于Flutter的文章总结了下标签+导航的项目模式的搭建,具体的有需要的可以去看看Flutter分类的文章,这篇文章我们简单的总结一下关于Flutter本地文件引用以及简单的自定义List的使用,我们先总结本地图片的引用。今天这篇文章的具体的UI效果如下:
引用本地图片
我们没有使用到的我们暂时先不提,等后面慢慢补充进去,比如说网络图片的显示等等,我们现总结一下关于本地图片的使用,具体的我们需要下面几步:
1、创建文件导入资源
Flutter的文件资源需要我们创建一个文件去管理,我们可以定义一个images的文件,当然这个名字不是固定的但需要留意下它的文件等级,它里面还可以装别的其他资源文件,你要叫Resource也可以的。然后不管是做Android的还是iOS的都知道我们的图片资源是分2x和3x的,所以我们在你创建的文件下面再创建一个2.0x和3.0x的文件夹分别存放相应倍数的图片资源。如下图:
2、不是说直接导入就能直接使用的,还需要处理一下 pubspec.yaml文件,具体的改动如下面所示:
# The following section is specific to Flutter. 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 # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware. # For details regarding adding assets from package dependencies, see # https://flutter.dev/assets-and-images/#from-packages # To add custom fonts to your application, add a fonts section here, # in this "flutter" section. Each entry in this list should have a # "family" key with the font family name, and a "fonts" key with a # list giving the asset and other descriptors for the font. For # example: # fonts: # - family: Schyler # fonts: # - asset: fonts/Schyler-Regular.ttf # - asset: fonts/Schyler-Italic.ttf # style: italic # - family: Trajan Pro # fonts: # - asset: fonts/TrajanPro.ttf # - asset: fonts/TrajanPro_Bold.ttf # weight: 700 # # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages #### 图片资源 assets: - images/icon_header.png - images/icon_heath.png - images/icon_village.png - images/mine_train.png
前面我们说的文件夹名字不是固定的,绑定使用的的就是assets,这里其实你理解成图片在项目中的层级位置就可以了。看上面Flutter给的注释信息,我们完全可以在导入别的信息,如 fonts等等。
3、现在可以直接使用了,它的使用还是相对比较简单的,我们直接上代码,需要注意的点是使用的时候需要的是图片全程,记得带上后缀。
Image.asset( "images/icon_header.png", width: 20, height: 20, )
实现上面我的页面
上面的实现我们需要简单的了解几个相应的控件,我们一个一个的介绍一下先,最后就可以出来我们前面的我的页面的UI了。
1、InkWell 它是一个效果控件 ,点击有泼墨的水波的效果 ,经常和Material+颜色透明一起使用。
2、Row 水平组件,首先一点是不管是我们现在说的Row还是我们后面说的 Column只能在继承与StatelessWidget或者StatefullWidget的Widget中使用。
Row({ Key key, MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, MainAxisSize mainAxisSize = MainAxisSize.max, CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, TextDirection textDirection, VerticalDirection verticalDirection = VerticalDirection.down, TextBaseline textBaseline = TextBaseline.alphabetic, Listchildren = const [], }) : super( children: children, key: key, direction: Axis.horizontal, mainAxisAlignment: mainAxisAlignment, mainAxisSize: mainAxisSize, crossAxisAlignment: crossAxisAlignment, textDirection: textDirection, verticalDirection: verticalDirection, textBaseline: textBaseline, );
它就这么一个构造方法,还是相对比较简单的,首先它是不能滚动的,但是它可以灵活布局,如果要让某个子组件填充满剩余剩余空间,可以在 children 中使用 Expanded 组件,children 这个属性看上面的源码我们知道,它是一个 Widget 数组,这个我们在后面会使用到。具体的它里面的 Expanded 组件我们下面总结,现不在这里细说。
它里面的属性还是相对比较简单的,可以自己了解学习一下。我们接着看下面的组件。
3、Column
上面说的是水平的,那这个肯定就是竖直的了,其实它俩挺像的,也都是最基础的。我们把它初始化方法放出来对比一下,对比一下前面的Row:
Column({ Key key, MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, MainAxisSize mainAxisSize = MainAxisSize.max, CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, TextDirection textDirection, VerticalDirection verticalDirection = VerticalDirection.down, TextBaseline textBaseline, Listchildren = const [], }) : super( children: children, key: key, direction: Axis.vertical, mainAxisAlignment: mainAxisAlignment, mainAxisSize: mainAxisSize, crossAxisAlignment: crossAxisAlignment, textDirection: textDirection, verticalDirection: verticalDirection, textBaseline: textBaseline, ); }
4、Expanded
这个我们也需要说说的,因为我们的Row和Column都需要Expanded,其实它也是比较简单的:
class Expanded extends Flexible { /// Creates a widget that expands a child of a [Row], [Column], or [Flex] /// so that the child fills the available space along the flex widget's /// main axis. const Expanded({ Key key, int flex = 1, @required Widget child, }) : super(key: key, flex: flex, fit: FlexFit.tight, child: child); }
我们就需要了解两个参数一个 flex 和一个 child,child我们就不多说了,比较简单,flex 按照一个比例理解,比如两个Expanded , 并排需要控制它们的宽度比例,就可以使用这个属性。这些我们在下面的代码使用中都有体现的。我们代码中细说。
我们看具体的代码,理解了上面大概说的,这个代码就相对比较简单了,我们先看看顶部的头部我们是怎样定义的:
class MineHeader extends StatelessWidget { String userHeaderImage; String userName; /// 这里定义了就可以在外面使用这个方法进行初始化 MineHeader(this.userHeaderImage, this.userName); @override Widget build(BuildContext context) { return Container( color: Colors.orange, height: 100, /// 水平布局 /// 在Row中使用Expanded的时候,无法指定Expanded中的子组件的宽度width,但可以指定其高度height。 /// 同理,在Column中使用Expanded的时候, /// 无法指定Expanded中的子组件的高度height,可以指定宽度width。 child: Row( children:[ Expanded( flex: 1, child: Container( padding: EdgeInsets.only(left: 15), child: Image.asset( userHeaderImage, width: 50, height: 50, ), ), ), Expanded( flex: 5, child: Container( padding: EdgeInsets.only(left: 15), child: Text( userName, /// 18号 蓝色 加粗 style: TextStyle( fontSize: 18, color: Colors.white, fontWeight: FontWeight.bold), ), ), ), ], ), ); } }
需要我们注意的点我们都在上面代码注释中基本上都说了,接下来我们我们看看下面列表的代码:
class MineItemWidget extends StatelessWidget { String imageName; String title; @required VoidCallback onTap; MineItemWidget(this.imageName, this.title, {this.onTap}); @override Widget build(BuildContext context) { return Container( /// 子视图 颜色容器 child: Column( children:[ /// Container( height: 53, child: _mineItem(imageName, title), ), Container( color: Color(0xffeaeaea), constraints: BoxConstraints.expand(height: 1.0), ), ], ), ); } Widget _mineItem(String imageName, String title) { return InkWell( onTap: () { this.onTap(); }, child: Row( /// children: [ Expanded( flex: 1, child: Container( padding: EdgeInsets.only(left: 15), child: Image.asset( imageName, width: 20, height: 20, ), ), ), Expanded( flex: 6, child: Container( padding: EdgeInsets.only(left: 15), child: Text( title, style: TextStyle(fontSize: 16), ), ), ), ], ), ); } }
最后就是把它俩写到我们的 MinePage ,MinePage的代码如下:
class MinePage extends StatefulWidget { MinePage({Key key}) : super(key: key); @override _MinePageState createState() => _MinePageState(); } class _MinePageState extends State{ @override Widget build(BuildContext context) { return Scaffold( appBar: new AppBar( title: Text("我的"), backgroundColor: Colors.orange, elevation: 0, // 去掉Appbar底部阴影 ), body: ListView( children: [ /// 头部的View MineHeader("images/icon_header.png", "张旭旭旭旭啊"), /// 线 _listViewLine, MineItemWidget("images/icon_heath.png", "我的健康", onTap: () { /// 跳转到查看按钮内容界面 Navigator.push( context, MaterialPageRoute(builder: (context) => MineCustomButton()), ); }), MineItemWidget("images/icon_village.png", "我的旅行", onTap: () { print('我的旅行'); }), _listViewLine, MineItemWidget("images/mine_train.png", "我的家乡", onTap: () { print('我的家乡'); }), ], ), ); } /// 分割线 Widget get _listViewLine { return Container( color: Color(0xffeaeaea), height: 6, ); } }
这样整个一个基本的效果就出来了,如最开始我们给的那张图效果一样。打算在后面一篇文章中再说说我们常见到一些组件,按钮,输入框,进度条等等的。
参考文章:
1、 Flutter ListView 自定义
2、详解flutter中本地资源图片的使用
3、项目地址