Flutter——自适应设计
class People{
final String name;
final String age;
final String address;
final String phone;
final String picture;
const People(this.name,this.age,this.address,this.phone,this.picture);
}
将集合Map中的数据转为List形式
final List peoples = peopleMap.map((e) => People(
e['name'].toString(),
e['age'].toString(),
e['address'].toString(),
e['phone'].toString(),
e['picture'].toString()))
.toList(growable: false);
下列一组Map数据是为啦实验这个Demo做的一组模拟数据
final List
通过LayoutBuilder
组件构造两个布局,通过获取当前屏幕宽度大小,如果小于限定值则采用窄布局,反之,采用宽布局
class _MyHomePageState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: LayoutBuilder(
builder: (context,constraints){
//maxWidth = 428.0
if(constraints.maxWidth < 500){
return const NarrowLayout();
}else{
return const WidthLayout();
}
},
),
);
}
}
通过采用Row
布局,形成一个一行两列的排列,然后通过Expanded
,去控制两列大小权重;左列为一个ListView
列表,右列为点击左列Ietm回调的相关数据;其中需要注意的时左列personCallBack
为一个回调方法,回调结果为当前被点击的Item实体类数据,然后做了一个setState
刷新行为,将数据赋值给变量_person
,让其方便展示右列数据
/// 假如屏幕宽度大于限定值后,采用此布局
/// 例如:当屏幕旋转后,此时宽度为之前的高度*/
class WidthLayout extends StatefulWidget {
const WidthLayout({Key? key}) : super(key: key);
@override
State createState() => _WidthLayoutState();
}
class _WidthLayoutState extends State {
People? _person;
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(flex: 2,
child: PeopleList(personCallBack: (person) => setState(() { _person = person;})),),
Expanded(flex: 3,
child: _person == null ? const Placeholder():PeopleDetail(person: _person!))
],
);
}
}
左边展示一个联系人列表,并定义啦一个personCallBack
接口,然后在Item的点击方法内实现了此方法,将此方法进行传递,联系上文,所描述的右列展示数据来源于此
class PeopleList extends StatelessWidget {
final void Function(People) personCallBack;
const PeopleList({Key? key,required this.personCallBack}) : super(key: key);
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: peoples.length,
itemBuilder: (context,index){
return ListTile(
title: Text(peoples[index].name,style: const TextStyle(fontSize: 18.0,fontWeight: FontWeight.bold)),
subtitle: Text(peoples[index].age,style: const TextStyle(fontSize: 14.0,fontWeight: FontWeight.normal)),
leading: const Icon(Icons.people_alt_outlined),
onTap: (){personCallBack(peoples[index]);},
);
}
);
}
}
右列先暂且不进行阐述,因为右列又对高度进行了自适应设计,具体内容放到后文的高度自适应中进行描述
class PeopleDetail extends StatelessWidget {
final People person;
const PeopleDetail({Key? key,required this.person}) : super(key: key);
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context , constraints ) {
if(constraints.maxHeight > 500){
return _HeightLayout(person: person);
}else{
return _ShortLayout(person: person);
}
},
);
}
}
宽布局是将两个页面放到了一个页面中,而窄布局是将其分为两个页面,通过Item点击方法进行路由传值进行展示
左侧列表与上述宽布局列表代表一样,通过复用方法减少代码量;但是值得注意的是,在上述的宽布局图片展示中,右列并没有导航栏,而在窄布局的时候就产生导航栏,重点是在通过路由传值的时候,构造了Scaffold
脚手架,并设置了一个导航栏,联系人详情页也是复用的,在声明的时候默认没有导航栏,所以在宽布局时,右侧没有导航栏
class NarrowLayout extends StatelessWidget {
const NarrowLayout({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return PeopleList(personCallBack: (person) {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => Scaffold(
appBar: AppBar(title: const Text('联系人详情')),
body: PeopleDetail(person: person))));
});
}
}
通过LayoutBuilder
组件构造两个布局,通过获取当前屏幕高度大小,如果小于限定值则采用短布局,反之,采用高布局
class PeopleDetail extends StatelessWidget {
final People person;
const PeopleDetail({Key? key,required this.person}) : super(key: key);
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context , constraints ) {
if(constraints.maxHeight > 500){
return _HeightLayout(person: person);
}else{
return _ShortLayout(person: person);
}
},
);
}
}
当屏幕高度大于限定值时,图片、电话、地址按纵向排列
class _HeightLayout extends StatelessWidget {
final People person;
const _HeightLayout({Key? key,required this.person}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.network(person.picture,fit: BoxFit.cover,width: 100.0,height: 100.0),
Text(person.phone,style: const TextStyle(fontSize: 14.0,fontWeight: FontWeight.normal)),
Text(person.address,style: const TextStyle(fontSize: 16.0,fontWeight: FontWeight.bold))
],
),
);
}
}
当屏幕高度小于限定值时,图片与电话和地址呈横向排列,电话和地址呈纵向排列
class _ShortLayout extends StatelessWidget {
final People person;
const _ShortLayout({Key? key,required this.person}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: Row(
children: [
Image.network(person.picture,fit: BoxFit.cover,width: 200.0,height: 200.0),
Expanded(child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(person.phone,style: const TextStyle(fontSize: 14.0,fontWeight: FontWeight.normal)),
Text(person.address,style: const TextStyle(fontSize: 16.0,fontWeight: FontWeight.bold))
],
)
)
],
),
);
}
}
在Flutter进行MacOS端开发时,如果加载网络图片显示异常,则需要添加网络权限,在macos
->Runner
->DebugProfile.entitlements
中添加如下语句
com.apple.security.network.client