美文网首页
flutter 四布局开发

flutter 四布局开发

作者: crossroads | 来源:发表于2024-05-14 15:56 被阅读0次

    前言

    文本Text /按钮icon等这些基础的用的时候再看就行,主打一个快速入门。
    点击右上角运行,但有时候动态更新不行,需要点红色框停止再运行一次。

    一、轮播图示例

    1. 创建banner_demo.dart ,文件名要用小写哦
      输入stless生成快捷模版,在爆红的地方用alt+enter快捷键import相应的库。
    import 'package:flutter/material.dart';
    class BannerDemoApp extends StatelessWidget {
      const BannerDemoApp({super.key});
      @override
      Widget build(BuildContext context) {
        return const Placeholder();
      }
    }
    
    1. 搜索轮播图的实现用的pageview控件。大多数 App 都包含 Tab 换页效果、图片轮动以及抖音上下滑页切换视频功能等等,这些都可以通过 PageView 轻松实现。
     @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Container(
            color: Colors.blue,
            child: PageView.builder(itemBuilder: (_, index) {
              return Center(
                  child: Text(
                    "当前位置 $index",
                    style: const TextStyle(
                        color: Colors.black, fontSize: 23, fontFamily: 'PingFang SC', decoration: TextDecoration.none),
                  ));
            },itemCount: 10),//itemCount如果不写,会不断地有新页面。
          ),
        );
      }
    

    可以使用快捷键command+鼠标进入想看的源码的介绍。

    1. 另一种写法
     @override
      Widget build(BuildContext context) {
        return const MaterialApp(
          home: _BannerPage(),
        );
      }
    
    
    class _BannerPage extends StatelessWidget {
      const _BannerPage({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Container(
          color: Colors.blue,
          child: PageView.builder(itemBuilder: (_, index) {
            return Center(
                child: Text(
              "当前位置 $index",
              style: const TextStyle(
                  color: Colors.black, fontSize: 23, fontFamily: 'PingFang SC', decoration: TextDecoration.none),
            ));
          },itemCount: 10),
        );
      }
    }
    
    1. 将布局从外部传入
    class BannerDemoApp extends StatelessWidget {
      const BannerDemoApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return const MaterialApp(
          home: _BannerPage(children: [
            Center(
                child: Text(
              "11111",
              style: TextStyle(color: Colors.red, fontSize: 23, fontFamily: 'PingFang SC', decoration: TextDecoration.none),
            )),
            Center(
                child: Text(
              "2222",
              style: TextStyle(
                  color: Colors.yellowAccent, fontSize: 23, fontFamily: 'PingFang SC', decoration: TextDecoration.none),
            )),
          ]),
        );
      }
    }
    
    class _BannerPage extends StatelessWidget {
      final List children;
      const _BannerPage({super.key, required this.children});
    
      @override
      Widget build(BuildContext context) {
        return Container(
          color: Colors.blue,
          child: PageView.builder(
              itemBuilder: (_, index) {
                return children[index];
              },
              itemCount: children.length),
        );
      }
    }
    
    1. 如果需要一个计时器自动轮播呢?这个时候StatelessWidget就不能满足需求了,需要用StatefulWidget .
      输入stful ,生成模版
    class AutoBanner extends StatefulWidget {
      const AutoBanner({super.key});
      @override
      State<AutoBanner> createState() => _AutoBannerState();
    }
    class _AutoBannerState extends State<AutoBanner> {
    final List<Widget> children = [
        const Center(
            child: Text(
              "11111",
              style: TextStyle(color: Colors.red, fontSize: 23, fontFamily: 'PingFang SC', decoration: TextDecoration.none),
            )),
        const Center(
            child: Text(
              "2222",
              style: TextStyle(
                  color: Colors.yellowAccent, fontSize: 23, fontFamily: 'PingFang SC', decoration: TextDecoration.none),
            )),
      ];
      @override
      Widget build(BuildContext context) {
        return MaterialApp(home:Container(color: Colors.blue,
        child:  PageView.builder(
          itemCount: children.length,//可有可无
          itemBuilder: (_, index) {
            return children[index];
          }
        )));
      }
    }
    
    1. 在_AutoBannerState类中重写initState方法进行数据的初始化等操作
      late Timer _timer;
      int _currentPage = 0;
      late PageController _pageController;
    
     @override
      void initState() {
        super.initState();
        _pageController = PageController(keepPage: true)
          ..addListener(() {
            _currentPage = _pageController.page?.round() ?? 0;
          });
    
        _timer = Timer.periodic(Duration(seconds: 3), (timer) {
          _currentPage++;
          _pageController.animateToPage(
            _currentPage,
            duration: Duration(milliseconds: 300),
            curve: Curves.easeIn,
          );
        });
      }
    
    1. 对pageView进行更改,变为可循环滚动,且将control赋值给pageView
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
            home: Container(
                color: Colors.blue,
                child: PageView.builder(
                    controller: _pageController,
                    itemBuilder: (_, index) {
                      return children[index % children.length];//改为可循环滚动
                    })));
      }
    
    1. 在dispose中销毁监听
      @override
      void dispose() {
        _timer.cancel();
        _pageController.dispose();
      }
    
    1. 在用户触摸pageview时不自动滚动,在外层加入Listener监听手势
     @override
      Widget build(BuildContext context) {
        return MaterialApp(
            home: Container(
                color: Colors.blue,
                child: Listener(
                    onPointerDown: (event) {
                      //手指按下,定时取消
                      _timer.cancel();
                    },
                    onPointerMove: (event) {},
                    onPointerUp: (event) {
                      //手指抬起,定时开启
                      startAutoScroll();
                    },
                    child: PageView.builder(
                        controller: _pageController,
                        itemBuilder: (_, index) {
                          return children[index % widget.children.length]; //改为可循环滚动
                        }))));
      }
    

    小提示:手势的监听还有GestureDetector

    区别:如果需要监听和处理低级别的原始指针事件数据,或者需要识别自定义手势,那么使用Listener更合适。
    如果你只需要响应一些常见的手势事件,如点击、双击、拖动等,使用GestureDetector会更加方便。

    这里用Listener更方便。

    1. 实现staartAutoScroll ,将timer的赋值提出来即可
      void startAutoScroll() {
        _timer = Timer.periodic(Duration(seconds: 3), (timer) {
          _currentPage++;
          _pageController.animateToPage(
            _currentPage,
            duration: Duration(milliseconds: 300),
            curve: Curves.easeIn,
          );
        });
      }
    
    1. 如果用户看不到页面希望可以暂停滚动。这个没有找到合适的类似安卓的onpause的方法,如果有知道的,欢迎评论哈。

    2. WidgetsBindingObserver判断应用是否处于前后台。处于后台暂停滚动。

    class _AutoBannerState extends State<AutoBanner> with WidgetsBindingObserver{
      late Timer _timer;
      int _currentPage = 0;
      late PageController _pageController;
    
      final List<Widget> children = [...  ];
    
      @override
      void initState() {
        super.initState();
        WidgetsBinding.instance.addObserver(this);
        _pageController = PageController(keepPage: true)
          ..addListener(() {
            _currentPage = _pageController.page?.round() ?? 0;
          });
        startAutoScroll();
      }
    
      void startAutoScroll() {
        _timer = Timer.periodic(Duration(seconds: 3), (timer) {
          _currentPage++;
          _pageController.animateToPage(
            _currentPage,
            duration: Duration(milliseconds: 300),
            curve: Curves.easeIn,
          );
        });
      }
    
    
      @override
      void didChangeAppLifecycleState(AppLifecycleState state) {
        super.didChangeAppLifecycleState(state);
        if (state == AppLifecycleState.resumed) {
          startAutoScroll();
        } else if (state == AppLifecycleState.paused) {
          _timer.cancel();
        }
      }
    
      @override
      void dispose() {
        _timer.cancel();
        _pageController.dispose();
        WidgetsBinding.instance.removeObserver(this);
      }
    
      @override
      Widget build(BuildContext context) {
        return ....
      }
    }
    

    暂时告一段落了,下面是setState用法,和上面轮播图没关系。

    setState用法,点击按钮变颜色

    bool white = false;
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
            home: Center(
                child: GestureDetector(
        
              child: Container(
                  height: 48,
                  margin: const EdgeInsets.all(12),
                  decoration: white
                      ? BoxDecoration(color: Colors.orange, borderRadius: BorderRadius.circular(18))
                      : BoxDecoration(color: Colors.blue, borderRadius: BorderRadius.circular(18)),
                  child: Center(
                    child: Text("点击这里改变背景色",
                        style: TextStyle(fontSize: 15, color: Colors.white, decoration: TextDecoration.none)),
                  )),
              onTap: () {
                setState(() {
                  white = !white;
                });
              },
            )));
      }
    

    二、提取子布局

    选中一个子布局,将其作为子widget,也就是子view


    class ChildWidget extends StatelessWidget {
      const ChildWidget({
        super.key,
      });
    
      @override
      Widget build(BuildContext context) {
        return Container(
          height: 48,
          child: Center(
              child: customText(16, "文案",
                  align: TextAlign.center, weight: FontWeight.w600)),
        );
      }
    }
    

    父View中

    child: ChildWidget()
    

    那么如果需要点击ChildWidget,更改child中的字体大小怎么做?
    和android中开发一样,使用方法回调。在子view中声明变量

      final ValueChanged<int> onChanged;
    //也等于
     final void Function(int) onChanged;
    

    子view中

    class ChildWidget extends StatelessWidget {
      final void Function(double) onChanged;
      final double textSize;
    
      const ChildWidget({
        super.key,
        required this.textSize,
        required this.onChanged,
      });
    
      @override
      Widget build(BuildContext context) {
        return GestureDetector(
            onTap: () {
              onChanged.call(38);
            },
            child: Container(
              height: 48,
              child: Center(child: customText(textSize, "文案", align: TextAlign.center, weight: FontWeight.w600)),
            ));
      }
    }
    
    

    父view中写一个_newValue全局变量。一定要记得setState,不然无法刷新的。

    ChildWidget(
              onChanged: (value) {
                setState(() {
                  _newValue = value;
                });
              }
    

    三、一些渐变色

    1. 文字改为渐变色
    
    Widget gradientText(double fontSize, String text, List<Color> colors,
        {FontWeight weight = FontWeight.bold, TextAlign align = TextAlign.left}) {
      return ShaderMask(
          shaderCallback: (bounds) {
            return LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: colors)
                .createShader(bounds);
          },
          blendMode: BlendMode.srcATop,
          child: Text(text, style: TextStyle(color: Colors.white, fontSize: fontSize)));
    }
    
    1. 背景渐变色
    BoxDecoration gradientBackground(List<Color> colors, BorderRadius? radius) {
      return BoxDecoration(
        gradient: LinearGradient(
          colors: colors,
        ),
        borderRadius: radius,
      );
    }
    
    

    四、高斯模糊

    Scaffold(
            appBar: AppBar(title: const Text('高斯模糊示例')),
            body: Container(
                width: 400,
                //只对其子Widget 起作用
                child: ImageFiltered(
                    imageFilter: ImageFilter.blur(sigmaX: 2, sigmaY: 2),
                    child: Image.network(
                      'http://pic1.win4000.com/wallpaper/9/594cc06f555e8.jpg',
                    ))));
    

    五、简便写法

    GestureDetector(
       behavior: HitTestBehavior.opaque,//空白区域可点击
                    onTap: controller.swipe,
                    child: Container(
                      height: 48,
                      width: 200,
                      alignment: Alignment.center,
                      decoration: BoxDecoration(borderRadius: BorderRadius.circular(24.0), color: Colors.red),
                      child: Text("按钮"),
                    )))
     swipe() {
    
      }
    

    六、文字超出一行...

    Text(
      'This is a long text that will be truncated',
      overflow: TextOverflow.ellipsis,
    )
    

    七、马赛克(毛玻璃)

      ClipRRect(
                                          borderRadius: BorderRadius.circular(8.0),
                                          child: Stack(
                                            fit : StackFit.expand,
                                            alignment: Alignment.center,
                                            children: [
                                              img,
                                              Container(
                                                alignment: Alignment.center,
                                                clipBehavior: Clip.hardEdge,
                                                decoration: BoxDecoration(borderRadius: BorderRadius.circular(10)),
                                                child: BackdropFilter(
                                                  filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
                                                  child: Container(color: Colors.white.withAlpha(0)),
                                                ),
                                              ),
                                            ],
                                          ),
                                        ),
    

    优化参考:https://www.jianshu.com/p/06b718fde8c4
    八、左文字...右紧挨图片

    Flexible(
                                            fit: FlexFit.loose,
                                            child:Text(
                                              controller.viewmodel.model.listObs[index].title ?? "",
                                              style: Get.theme.textTheme.titleMedium,
                                              maxLines: 1,
                                              overflow: TextOverflow.ellipsis,
                                            ))
    

    九、带白圈的圆形图片

     SizedBox(
                  width: 90,
                  height: 90,
                  child: Container(
                    decoration: BoxDecoration(
                      border: Border.all(
                        width: 2,
                        color: Colors.white,
                      ),
                      shape: BoxShape.circle,
                    ),
                    child: ClipOval(
                      child: cachedImage(
                          imageUrl: avatar, fit: BoxFit.cover),
                    ),
                  )),
    

    十、图片左右重叠

    Stack(
          alignment: Alignment.center, // 对齐方式
          children: [
            Positioned(
              left: 20, // 调整左侧图片的位置
              child: ClipOval(
                child: Image.network(
                  'https://example.com/image1.jpg', // 替换为你的第一张图片URL
                  width: 150,
                  height: 150,
                  fit: BoxFit.cover,
                ),
              ),
            ),
            Positioned(
              right: 20, // 调整右侧图片的位置
              child: ClipOval(
                child: Image.network(
                  'https://example.com/image2.jpg', // 替换为你的第二张图片URL
                  width: 150,
                  height: 150,
                  fit: BoxFit.cover,
                ),
              ),
            ),
          ],
        )
    

    十一、加分割线的listview

     ListView.separated(
          itemCount: 20, // 设置 item 的数量
          separatorBuilder: (context, index) => Divider(
            thickness: 1.0,
            color: Colors.grey,
          ),
          itemBuilder: (context, index) {
            return Container(
              height: 52, // 设置每个 item 的高度
              alignment: Alignment.center, // 垂直和水平居中
              child: Text(
                'Item $index', // 显示文本
                style: TextStyle(fontSize: 16.0),
              ),
            );
          },
        )
    

    十二、gridview

    GridView.builder(
                     padding: const EdgeInsets.fromLTRB(24, 0, 24, 0),
                     gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                       crossAxisCount: 5, // Number of columns
                       crossAxisSpacing: 12, // Spacing between columns
                       mainAxisSpacing: 12,
                       mainAxisExtent: ((MediaQuery.of(context).size.width - 100
                     )
    ...
    )
    

    十三、pageview禁止左右滑

          physics: NeverScrollableScrollPhysics(),
    

    后记

    开发过程,看run命令栏,排查问题


    相关文章

      网友评论

          本文标题:flutter 四布局开发

          本文链接:https://www.haomeiwen.com/subject/ajqefjtx.html