• Flutter——自适应设计


    效果视频

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    转为List

    将集合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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Map数据

    下列一组Map数据是为啦实验这个Demo做的一组模拟数据

    final List> peopleMap =
        [
          {
            "name": "FranzLiszt",
            "age": "21",
            "sex": "male",
            "address": "湖南省xxxxxxxxxxxxxxx",
            "phone": "17311112222",
            "picture": "https://puui.qpic.cn/vstar_pic/0/name_77904_688t1467970955.jpg/0?max_age=7776000"
          },
          {
            "name": "Jack",
            "age": "19",
            "sex": "male",
            "address": "湖北省xxxxxxxxxxxxxxx",
            "phone": "1733334444",
            "picture": "https://puui.qpic.cn/vstar_pic/0/name_94722_688t1505976146.jpg/0?max_age=7776000"
          },
          {
            "name": "Peter",
            "age": "35",
            "sex": "male",
            "address": "北京市xxxxxxxxxxxxxxx",
            "phone": "17344445555",
            "picture": "https://puui.qpic.cn/media_img/0/854561579417059/0?max_age=7776000"
          },
          {
            "name": "Smith",
            "age": "45",
            "sex": "male",
            "address": "河南xxxxxxxxxxxxxxx",
            "phone": "17344556666",
            "picture": "https://puui.qpic.cn/media_img/0/932661658913960/0?max_age=7776000"
          },
          {
            "name": "Garcia",
            "age": "21",
            "sex": "female",
            "address": "天津市xxxxxxxxxxxxxxx",
            "phone": "17366778899",
            "picture": "https://puui.qpic.cn/media_img/0/955481577176312/0?max_age=7776000"
          },
          {
            "name": "Rodriguez",
            "age": "17",
            "sex": "male",
            "address": "河北省xxxxxxxxxxxxxxx",
            "phone": "17322334455",
            "picture": "https://puui.qpic.cn/media_img/0/1093271609034985/0?max_age=7776000"
          },
          {
            "name": "Wilson",
            "age": "38",
            "sex": "male",
            "address": "江苏省xxxxxxxxxxxxxxx",
            "phone": "17311223344",
            "picture": "https://puui.qpic.cn/vstar_pic/0/name_77904_688t1467970955.jpg/0?max_age=7776000"
          },
          {
            "name": "Jones",
            "age": "44",
            "sex": "male",
            "address": "浙江省xxxxxxxxxxxxxxx",
            "phone": "17377889900",
            "picture": "https://puui.qpic.cn/vstar_pic/0/name_77904_688t1467970955.jpg/0?max_age=7776000"
          },
          {
            "name": "Miller",
            "age": "18",
            "sex": "female",
            "address": "四川省xxxxxxxxxxxxxxx",
            "phone": "17323233434",
            "picture": "https://puui.qpic.cn/vstar_pic/0/name_77904_688t1467970955.jpg/0?max_age=7776000"
          },
          {
            "name": "Davis",
            "age": "55",
            "sex": "male",
            "address": "重庆市xxxxxxxxxxxxxxx",
            "phone": "17334345656",
            "picture": "https://puui.qpic.cn/vstar_pic/0/name_77904_688t1467970955.jpg/0?max_age=7776000"
          }
        ];
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83

    宽度自适应

    通过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();
              }
            },
          ),
        );
      }
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    宽布局

    通过采用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!))
          ],
        );
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    左列

    左边展示一个联系人列表,并定义啦一个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]);},
              );
            }
        );
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    右列

    右列先暂且不进行阐述,因为右列又对高度进行了自适应设计,具体内容放到后文的高度自适应中进行描述

    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);
          }
         },
        );
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    窄布局

    宽布局是将两个页面放到了一个页面中,而窄布局是将其分为两个页面,通过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))));
        });
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    高度自适应

    通过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);
          }
         },
        );
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    高布局

    当屏幕高度大于限定值时,图片、电话、地址按纵向排列

    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))
            ],
          ),
        );
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    低布局

    当屏幕高度小于限定值时,图片与电话和地址呈横向排列,电话和地址呈纵向排列

    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))
                ],
              )
              )
            ],
          ),
        );
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在Flutter进行MacOS端开发时,如果加载网络图片显示异常,则需要添加网络权限,在macos->Runner->DebugProfile.entitlements中添加如下语句

    com.apple.security.network.client
    
    
    • 1
    • 2
  • 相关阅读:
    钱小雨--进
    CDGP与CDMP考哪个合适?
    【JavaScript总结】双等与三等
    基本算法——二分查找
    Sharding-JDBC分库分表-自定义分片算法-4
    医药行业数智化供应链管理系统:完善企业管理水平,驱动医药供应链协同发展
    JVM内存管理
    市面上最适合跑步用的耳机有哪些、分享五款最优秀的跑步耳机
    Spring Security 中重要对象汇总
    集成应用签名服务,加入签名计划后,想要删除AGC中托管的应用签名,退出签名计划如何做?应用签名服务常见问题小集合
  • 原文地址:https://blog.csdn.net/News53231323/article/details/127753506