概述
这几天一直在学习, 今天有时间整理一下学习的内容用于记录与分享,详细的控件使用描述有兴趣的可以去官网上看,我这边自己写了一个很简单的小demo,包含了一些基础的知识。
surprised.png
记录的知识点:
· 1 底部菜单导航
· 2 页面的跳转
· 3 ListView
· 4 吐司(这个是内部实现引用的,具体flutter自带的框架,暂时不清楚)
· 5 涉及到一些布局的书写(属性)
· 6 res资源的引用
· 7 涉及到的一些widget使用介绍,或在注解或在代码片段后。
项目效果图:
home.png
一、页面的跳转
我自己写了一个页面就放了一个RaisedButton,跳转到首页。
transition.png
RaisedButton就是一个button,实现onPressed监听btn事件。
Navigator这个是用来进行跳转页面的。
涉及到的一些需要介绍的控件我用都**来表示注解了, //看不清楚
import 'package:flutter/material.dart'; **基本多是这个包
import 'package:flutter_app/MainActivity.dart'; **我跳转的首页面
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
return new MaterialApp(**MaterialApp个人理解为程序的渲染入口
title: 'Hello World',
theme: new ThemeData( **全局主题只是由应用程序根MaterialApp创建的Theme来表示
primaryColor: Colors.lightBlue,
),
home: new RandomWords(),**调用方法体
);
}
}
class RandomWordsState extends State{
**Scaffold 是 Material library 中提供的一个widget,
它提供了默认的导航栏、标题和包含主屏幕widget树的body属性。widget树可以很复杂。
** Center这个空间居中
** RaisedButton = button
** Navigator页面的跳转,差不多都是这个写法,固定
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new RaisedButton(
child: new Text('登录'),
onPressed: (){
Navigator.push(context, new MaterialPageRoute(builder: (context)=>new MainActivity()));
}),
),
);
}
}
class RandomWords extends StatefulWidget {
@override
** =>单行函数或方法的简写
createState() => new RandomWordsState();
}
其中StatelessWidget它是表示所有的属性都是最终的,可以理解为属性不可变。
StatefulWidget 我觉得可以理解为android中某一个自定义的方法(代码书写ui),方法体的内容是可变的,当然它也是一个widget。在flutter中书写一个这样的方法,就需要按照上述代码中的方式来书写。
二、MainActivity页面
images文件的引用。
页面中包含了一些图片资源,记录一下images的引用方式。
项目结构图.png
一开始Flutter是没有images文件夹的,自己创建一个,跟android ios保持同级。
在pubspec.yaml文件中进行images的关联,pubspec.yaml这个可以理解为build.gradle。
引用界面.png
在Flutter的节点下新增(引用全目录 ,若单张全名称包含后缀):
assets:
- images/
添加完毕后在右上角有同步按钮,别忘了
image.png
package get 加载引入的包
package upgradle 升级包
flutter upgradle 整理升级 包括Dart SDK version等
flutter doctor 检测需要安装的东西
包的引用
当初在看官方的时候,引用了一个english_words的包,项目中没有用,但是 这边记录一下引用包的方式。
image.png
在 lib/main.dart 中 import 'package:english_words/english_words.dart';就可以了,需要注意的是,在pubspec.yaml中添加了之后记得package get,在弹出的message窗口中Process finished with exit code 0 表示引用成功。
底部导航 BottomNavigationBarItem
在看代码之前:
此处简要一下代码的书写逻辑。
因为页面是可变可调整的,所以我肯定需要书写StatefulWidget。
接着 初始了切换的图片,文字等资源
在BottomNavigationBarItem中主要是通过 下标 切换图片和文字的显示,当然也包含切换页面,切换页面的书写方式类似android中的fragment,属于独立页面,配合使用IndexedStack进行切换页面的显示与隐藏。
具体的代码含义,我在注释里进行介绍。
import 'package:flutter/cupertino.dart'; **底部导航切换,需导入
import 'package:flutter/material.dart'; **上面注解介绍过
import 'package:flutter_app/page/homeinfo.dart';**fragment页面
import 'package:flutter_app/page/myinfo.dart';**fragment页面
void main() => runApp(new MainActivity());
class MainActivity extends StatelessWidget {
**这块没啥介绍的, 同上
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Hello World',
theme: new ThemeData(
primaryColor: Colors.blue,
),
home: new RandomWords(),
);
}
}
class RandomWordsState extends State {
int _tabIndex = 0; ** 默认当前页
// static const double IMAGE_ICON_WIDTH = 30.0; 标题上的返回按钮
// static const double ARROW_ICON_HEIGHT = 16.0; 标题上的返回按钮
final normalTextColor = new TextStyle(color: const Color(0xff969696)); **默认的颜色
final selectTextColor = new TextStyle(color: const Color(0xff63ca6c)); **选择的颜色
var tabImage; **切换的image
var _body; **IndexedStack的对象
var tabNameList = ['首页', '地图', '我的']; **底部导航名称
var titleNameList = ['动服务平台', '地图', '我的']; **标题名称
// var leftIcon;标题上的返回按钮
// RandomWordsState(){
// leftIcon = setImages("images/icon_left.png");
// }
**统一设置image属性 ,path为images的引用路径
Image getImagePath(path) {
return new Image.asset(
path,
width: 20.0,
height: 20.0,
);
}
**切换图片的初始化,包括切换页面的body初始 , getImagePath为统一设置的images属性。
void initData() {
if (tabImage == null) {
tabImage = [
[
getImagePath('images/activity_home_unchecked.png'),
getImagePath('images/activity_home_checked.png')
],
[
getImagePath('images/activity_map_unchecked.png'),
getImagePath('images/activity_map_checked.png')
],
[
getImagePath('images/activity_mine_unchecked.png'),
getImagePath('images/activity_mine_checked.png')
],
];
}
** children这个我个人理解它是一个组合控件,像是一个容器,可以包含很多不同的Ui,然后拼凑到一起。
_body = new IndexedStack(
children: [new HomeInfo(), new MyInfo(), new MyInfo()],
index: _tabIndex,
);
}
** 根据下标返回 text的颜色值
TextStyle getTabTextStyle(int curIndex) {
if (curIndex == _tabIndex) {
return selectTextColor;
}
return normalTextColor;
}
** 调用getTabTextStyle 根据下标设置text的颜色值
Text getTabTitle(int curIndex) {
return new Text(tabNameList[curIndex], style: getTabTextStyle(curIndex));
}
** 返回当前下标的images中的 所选图片
Image getTabIcon(int curIndex) {
if (curIndex == _tabIndex) {
return tabImage[curIndex][1];
}
return tabImage[curIndex][0];
}
//设置iamge的位置
// Widget setImages(path) {
// return new Padding(
// padding: const EdgeInsets.fromLTRB(0.0, 0.0, 10.0, 0.0),
// child: new Image.asset(path,
// width: IMAGE_ICON_WIDTH, height: ARROW_ICON_HEIGHT));
// }
** AppBar标题{title标题文字 {Center标题位置 child{标题内容} } }
** body切换的index页面
** bottomNavigationBar底部导航{items导航数组{0,1,2}}
**onTap点击(Index作为点击返回值) {setState通知框架状态已经改变{_tabIndex 赋值当前Index}}
@override
Widget build(BuildContext context) {
initData();
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Center(
child: new Text(titleNameList[_tabIndex],
style: new TextStyle(color: Colors.white)),
// child: new Row(
// children: [
// leftIcon,
// new Text(tabNameList[_tabIndex],
// style: new TextStyle(color: Colors.white))
// ],
// ),
),
iconTheme: new IconThemeData(color: Colors.white)),
body: _body,
bottomNavigationBar: new CupertinoTabBar(
items: [
new BottomNavigationBarItem(
icon: getTabIcon(0),
title: getTabTitle(0)),
new BottomNavigationBarItem(
icon: getTabIcon(1),
title: getTabTitle(1)),
new BottomNavigationBarItem(
icon: getTabIcon(2),
title: getTabTitle(2)),
],
currentIndex: _tabIndex,
onTap: (index) {
setState(() {
_tabIndex = index;
});
},
),
),
);
}
}
class RandomWords extends StatefulWidget {
@override
createState() => new RandomWordsState();
}
底部切换基本就这些,你要是只是想测试一下底部切换效果也可以像我indexedStack那样一样,除了首页,剩下的复用。
HomeInfo.dart页面
HomeInfo.png
考虑到页面的布局,我分成了2个层级,网格显示是一个层级,报表是另外的一个层级,可以说是ListView 的两个item,只不过是不同的item布局。当然这只是我个人的想法,经过思考后在StatefulWidget,我返回的就是一个ListView .
var title = ["项目信息", "农村公路建设统计报表", "路网结构改造统计报表"];
@override
Widget build(BuildContext context) {
var listview = new ListView.builder(
itemCount: title.length, itemBuilder: (context, i) => renderRow(i));
return listview;
}
ListView 初始化:
itemcount 网格是一个单独的布局,另外的两个可以复用布局。
renderRow是我自定义的方法
i 算是0 、1、 2,其中1、2布局复用。
renderRow(int i) {
if (i == 0) { ** 此处i=0初始化网格的样式。
var projectInfo = new Container(
// color: const Color.fromRGBO(255, 255, 255, 255.0),
decoration: new BoxDecoration(
color: Colors.white,
),
child: new Center(
child: new Column(
children: [
new Text(
title[0],
textAlign: TextAlign.left,
),
new Container(
color: const Color.fromRGBO(240, 248, 255, 200.0),
child: new Row(
children: [
new Expanded(
flex: 1,
child: new Container(
margin: const EdgeInsets.only(
top: 10.0, right: 5.0, left: 10.0, bottom: 10.0),
decoration: new BoxDecoration(
border: new Border.all(
width: 1.0, color: Colors.black12),
borderRadius: const BorderRadius.all(
const Radius.circular(10.0))),
height: 100.0,
child: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
new IconButton(
icon: new Image.asset(
"images/icon_way.png",
width: 50.0,
height: 50.0,
),
onPressed: () {
showShort("农村公路建设类项目");
}),
new Center(child: new Text("农村公路建设类项目"))
],
),
),
)),
new Expanded(
flex: 1,
child: new Container(
margin: const EdgeInsets.only(
top: 10.0, right: 10.0, left: 5.0, bottom: 10.0),
decoration: new BoxDecoration(
border: new Border.all(
width: 1.0, color: Colors.black12),
borderRadius: const BorderRadius.all(
const Radius.circular(10.0))),
height: 100.0,
child: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
new IconButton(
icon: new Image.asset(
"images/icon_reform.png",
width: 50.0,
height: 50.0,
),
onPressed: () {
showShort("农村公路建设类项目");
}),
new Center(child: new Text("危桥改造类项目"))
],
)),
))
],
),
),
new Container(
child: new Row(
children: [
new Expanded(
flex: 1,
child: new Container(
margin: const EdgeInsets.only(
top: 0.0, right: 5.0, left: 10.0, bottom: 10.0),
decoration: new BoxDecoration(
border: new Border.all(
width: 1.0, color: Colors.black12),
borderRadius: const BorderRadius.all(
const Radius.circular(10.0))),
height: 100.0,
child: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
new IconButton(
icon: new Image.asset(
"images/icon_security.png",
width: 50.0,
height: 50.0,
),
onPressed: () {
showShort("县乡安防工程类项目");
}),
new Center(child: new Text("县乡安防工程类项目"))
],
),
),
)),
new Expanded(
flex: 1,
child: new Container(
margin: const EdgeInsets.only(
top: 0.0, right: 10.0, left: 5.0, bottom: 10.0),
decoration: new BoxDecoration(
border: new Border.all(
width: 1.0, color: Colors.black12),
borderRadius: const BorderRadius.all(
const Radius.circular(10.0))),
height: 100.0,
child: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
new IconButton(
icon: new Image.asset(
"images/icon_security_green.png",
width: 50.0,
height: 50.0,
),
onPressed: () {
showShort("村道安防工程类项目");
}),
new Center(child: new Text("村道安防工程类项目"))
],
)),
))
],
),
),
],
),
),
);
return new GestureDetector(
child: projectInfo,
);
}
new Container(
child: new Text(title[i]),
);
var listCountItem = new Padding(
padding: const EdgeInsets.fromLTRB(15.0, 10.0, 0.0, 10.0),
child: new Column(
children: [
new Container(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
new Text(title[i]),
new Container(
height: 200.0,
child: new ListView(
children: [
new ListTile(
leading: new Icon(Icons.map),
title: new Text('Maps'),
),
new ListTile(
leading: new Icon(Icons.photo_album),
title: new Text('Album'),
),
new ListTile(
leading: new Icon(Icons.phone),
title: new Text('Phone'),
),
],
),
)
],
),
),
// new ListView(
// children: [
// new ListTile(
// title: new Text('123123'),
// )
// ],
// )
],
),
);
return new InkWell(
child: listCountItem,
onTap: () {
showShort("1111");
},
);
}
部分控件属性介绍:
Container也是一个widget,允许自定义其子widget。
个人理解就是一个容器,可以添加一些别的widget组合成想要的ui样式 。可添加填充,边距,边框或背景色。
BoxDecoration这个类似shape可以操作内填充颜色,圆角等
Expanded 这个可以理解为权重,flex表示当前包裹控件所在父布局的权重比例。
children: []这个属性相当于多个Item一样,数组中的每一个值都可以看做成一个Item,具体什么样的ui样式看你自己怎么写。
mainAxisAlignment和crossAxisAlignment属性用来对齐其子项。 对于行(Row)来说,主轴是水平方向,横轴垂直方向。对于列(Column)来说,主轴垂直方向,横轴水平方向。具体的详细参数,对照官网。
GestureDetector这个是用来检测用户做出的手势,点击的时候会回调onTap,我这边没有写onTap,我是单独在iconButton中做的点击处理。写的有点问题,不过顺带的介绍一下这个。
InkWell实现水波纹,边框效果,跟BoxDecorationc差不多。
关于吐司showShort,分享实现方式(改的原生):
Android
Android.png
new MethodChannel(getFlutterView(), "com.coofee.flutterdemoapp/sdk/toast")
.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
if ("show".equals(methodCall.method)) {
String text = methodCall.argument("text");
int duration = methodCall.argument("duration");
Toast.makeText(MainActivity.this, text, duration).show();
}
}
});
IOS
image.png
#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;
FlutterMethodChannel* toastChannel = [FlutterMethodChannel
methodChannelWithName:@"com.coofee.flutterdemoapp/sdk/toast"
binaryMessenger:controller];
[toastChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
if ([@"show" isEqualToString:call.method]) {
// 展示toast;
NSLog(@"显示toast....")
}
}];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
Flutter
import 'package:flutter/services.dart';
// 下划线开头的变量只在当前package中可见。
const _toast = const MethodChannel('com.coofee.flutterdemoapp/sdk/toast');
const int _LENGTH_SHORT = 0;
const int _LENGTH_LONG = 1;
void show(String text, int duration) async {
try {
await _toast.invokeMethod("show", {'text': text, 'duration': duration});
} on Exception catch (e) {
print(e);
} on Error catch (e) {
print(e);
}
}
void showShort(String text) {
show(text, _LENGTH_SHORT);
}
void showLong(String text) {
show(text, _LENGTH_LONG);
}
MyInfo.dart页面
MyInfo.png
MyInfo相对上一个页面要简单不少,主要是ListView,剩下的就是一些资源的初始化。
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app/utils/toastdart.dart';
class MyInfo extends StatefulWidget {
@override
createState() => new MyInfoState();
}
class MyInfoState extends State {
static const double IMAGE_ICON_WIDTH = 30.0;
static const double ARROW_ICON_WIDTH = 16.0;
var inons = [];
var titleTextStyle = new TextStyle(fontSize: 16.0);
var title = ["用户指南", "地图设置", "路网数据", "数据备份", "项目数据更新", "关于系统", "退出登录"];
var images = [
"images/one.png",
"images/two.png",
"images/three.png",
"images/four.png",
"images/five.png",
"images/six.png",
"images/senven.png",
];
var rightIcon = new Image.asset(
"images/icon_right.png",
width: IMAGE_ICON_WIDTH,
height: ARROW_ICON_WIDTH,
);
MyInfoState() {
for (int i = 0; i < images.length; i++) {
inons.add(setImages(images[i]));
}
}
//设置iamge的位置
Widget setImages(path) {
return new Padding(
padding: const EdgeInsets.fromLTRB(0.0, 0.0, 10.0, 0.0),
child: new Image.asset(path,
width: IMAGE_ICON_WIDTH, height: ARROW_ICON_WIDTH));
}
@override
Widget build(BuildContext context) {
var listview = new ListView.builder(
itemCount: title.length , itemBuilder: (context, i) => renderRow(i));
return listview;
}
renderRow(int i) {
String itemName = title[i];
var itemCount = new Padding(
padding: const EdgeInsets.fromLTRB(25.0, 25.0, 25.0, 25.0),
child: new Row(
children: [
inons[i],
new Expanded(
child: new Text(
itemName,
style: titleTextStyle,
)),
rightIcon
],
),
);
return new InkWell(
child: itemCount,
onTap: (){
//toast
showShort(itemName);
// Navigator.of(context).push(new MaterialPageRoute(
// builder: (context)=> new MainActivity()));
},
);
}
}
EdgeInsets类似Android里面的margin。
总结:
万物皆Widget。
若看的不太舒服,望见谅···
最后
以上就是糟糕小熊猫为你收集整理的android布局实战,Android Flutter 构建布局UI实战(二)的全部内容,希望文章能够帮你解决android布局实战,Android Flutter 构建布局UI实战(二)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复