Flutter:gsy_flutter_demo项目学习——布局切换动画、列表滑动监听、列表滑动到指定位置、高斯模糊

前言

gsy_flutter_demo是一个关于各种小案例和小问题的方案解决。项目是由flutter大佬恋猫de小郭维护的

项目地址:https://github.com/CarGuo/gsy_flutter_demo

感兴趣的可以看一下大佬的文章:Flutter完整开发实战详解系列,GSY Flutter 系列专栏整合
在这里插入图片描述

关于该项目的学习呢,不会都学习,只会学习自己比较感兴趣的东西。

布局切换动画

动画效果

在这里插入图片描述
在这里插入图片描述

原文地址

Flutter 小技巧之有趣的动画技巧

代码

class YcHomeBody extends StatefulWidget {
    
    
  const YcHomeBody({
    
    Key? key, required this.width, required this.height})
      : super(key: key);
  // 容器的宽高
  final double width;
  final double height;
  
  State<YcHomeBody> createState() => _YcHomeBodyState();
}

class _YcHomeBodyState extends State<YcHomeBody>
    with SingleTickerProviderStateMixin {
    
    
  int currentIndex = 0;

  
  initState() {
    
    
    super.initState();

    //  创建吗一个定时器
    Timer.periodic(const Duration(seconds: 2), (timer) {
    
    
      setState(() {
    
    
        currentIndex += 1;
        if (currentIndex == 100) {
    
    
          currentIndex = 0;
        }
        // print("当前值:$currentIndex");
      });
    });
  }

  PositionedItemData getIndexPosition(int index, Size size) {
    
    
    switch (index) {
    
    
      case 0:
        return PositionedItemData(
          width: size.width / 2 - 5,
          height: size.height,
          left: 0,
          top: 0,
        );

      case 1:
        return PositionedItemData(
          width: size.width / 2 - 5,
          height: size.height / 2 - 5,
          left: size.width / 2 + 5,
          top: 0,
        );

      case 2:
        return PositionedItemData(
          width: size.width / 2 - 5,
          height: size.height,
          left: size.width / 2 + 5,
          top: size.height / 2 + 5,
        );
    }
    return PositionedItemData(
      width: size.width / 2 - 5,
      height: size.height,
      left: 0,
      top: 0,
    );
  }

  
  Widget build(BuildContext context) {
    
    
    return Center(
        child: Container(
      height: widget.height,
      width: widget.width,
      margin: const EdgeInsets.symmetric(horizontal: 20),
      // 根据父widget的约束条件来构建自身的布局,适合在需要根据父widget的约束条件来动态调整布局的场景中使用
      child: LayoutBuilder(
        builder: (BuildContext context, BoxConstraints constraints) {
    
    
          var f = getIndexPosition(currentIndex % 3, constraints.biggest);
          var s = getIndexPosition((currentIndex + 1) % 3, constraints.biggest);
          var t = getIndexPosition((currentIndex + 2) % 3, constraints.biggest);
          return Stack(
            fit: StackFit.expand,
            children: [
              // InkWell为其子widget提供水波纹效果和触摸事件处理
              PositionItem(f,
                  child: InkWell(
                    onTap: () {
    
    
                      // print("red");
                    },
                    child: Container(color: Colors.redAccent),
                  )),
              PositionItem(s,
                  child: InkWell(
                    onTap: () {
    
    
                      //print("green");
                    },
                    child: Container(color: Colors.greenAccent),
                  )),
              PositionItem(t,
                  child: InkWell(
                    onTap: () {
    
    
                      // print("yello");
                    },
                    child: Container(color: Colors.yellowAccent),
                  )),
            ],
          );
        },
      ),
    ));
  }
}

class PositionItem extends StatelessWidget {
    
    
  final PositionedItemData data;
  final Widget child;

  const PositionItem(this.data, {
    
    Key? key, required this.child})
      : super(key: key);

  
  Widget build(BuildContext context) {
    
    
    // AnimatedPositioned 用于动画定位位置的widget
    return AnimatedPositioned(
      duration: const Duration(seconds: 1), // 动画持续时间
      curve: Curves.fastOutSlowIn, // 动画曲线
      left: data.left,
      top: data.top,
      // AnimatedContainer,根据指定的时间段自动过渡其属性,用于实现大小的过渡
      child: AnimatedContainer(
        duration: const Duration(seconds: 1),
        curve: Curves.fastOutSlowIn,
        width: data.width,
        height: data.height,
        child: child,
      ),
    );
  }
}

// 用于规范元素的宽高和位置
class PositionedItemData {
    
    
  final double left;
  final double top;
  final double width;
  final double height;

  PositionedItemData({
    
    
    required this.left,
    required this.top,
    required this.width,
    required this.height,
  });
}

使用

YcHomeBody(width: 240, height: 300,)

列表滑动监听

class _MyHomePageState extends State<MyHomePage> {
    
    
  // 是否到底
  bool isEnd = false;
  // 偏移量
  int offset = 0;
  // 通知
  String notify = '';
  // 列表container
  final ScrollController _controller = ScrollController();

  
  void initState() {
    
    
    super.initState();

    _controller.addListener(() {
    
    
      setState(() {
    
    
        // 这里进行取整,不然小数位数太多
        offset = _controller.offset.floor();
        // 判断当前滚动位置的像素值是否等于可滚动区域最大值
        isEnd =
            _controller.position.pixels == _controller.position.maxScrollExtent;
      });
    });
  }

  
  Widget build(BuildContext context) {
    
    
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: NotificationListener(
          onNotification: (dynamic notification) {
    
    
            // 在这里处理滚动通知
            if (notification is ScrollStartNotification) {
    
    
              // 滚动开始
              notify = "滚动开始";
            } else if (notification is ScrollUpdateNotification) {
    
    
              // 滚动更新
              notify = "滚动更新";
            } else if (notification is ScrollEndNotification) {
    
    
              // 滚动结束
              notify = "滚动结束";
            }
            setState(() {
    
    });
            // 返回true表示阻止通知冒泡,返回false则继续向上传递通知
            return false;
          },
          child: ListView.builder(
              itemCount: 100,
              controller: _controller,
              itemBuilder: (context, index) {
    
    
                return Card(
                    child: Container(
                  height: 60,
                  alignment: Alignment.centerLeft,
                  child: Text("Item $index"),
                ));
              })),
      // 页脚按钮
      persistentFooterButtons: [
        Align(
          alignment: Alignment.center,
          child: Text("通知:$notify,偏移量:$offset,到达底部:${
      
      isEnd ? '是' : '否'}"),
        )
      ], // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

在这里插入图片描述
NotificationListener是一个widget,用于监听并处理各种通知。它可以将通知分发给子widget,并根据需要执行相应的操作

列表滑动到指定位置

作者提供了两种实现方式,一种是使用第三库,另一种是需要自己进行计算。本着能使用第三方库就使用第三库的原则,我们使用第三库来实现。

官方地址
https://pub-web.flutter-io.cn/packages/scroll_to_index

安装

flutter pub add scroll_to_index
class _MyHomePageState extends State<MyHomePage> {
    
    
  // 列表container
  late final AutoScrollController _controller;

  // 列表项高度,100~300间的整数
  List<int> items =
      List.generate(50, (index) => 100 + math.Random().nextInt(300 - 100));

  
  void initState() {
    
    
    super.initState();
    //  初始化
    _controller = AutoScrollController(
        viewportBoundaryGetter: () => Rect.fromLTRB(0, 0, 0,
            MediaQuery.of(context).padding.bottom), // 获取底部边界值,保证滚动到底部时,内容不会被遮挡
        axis: Axis.vertical);
  }

  
  Widget build(BuildContext context) {
    
    
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: ListView.builder(
          itemCount: 50,
          controller: _controller,
          itemBuilder: (context, index) {
    
    
            return AutoScrollTag(
                key: ValueKey(index),
                controller: _controller,
                index: index,
                highlightColor: Colors.black.withOpacity(0.1),
                child: Container(
                  height: items[index] + 0.0,
                  alignment: Alignment.topCenter,
                  margin: const EdgeInsets.all(10),
                  decoration: BoxDecoration(
                      border: Border.all(color: Colors.blue, width: 1),
                      borderRadius: BorderRadius.circular(10)),
                  child: Text("items ${
      
      index + 1},height:${
      
      items[index]}"),
                ));
          }),
      // 页脚按钮
      persistentFooterButtons: [
        ElevatedButton(
            onPressed: () async {
    
    
              // 滚动
              await _controller.scrollToIndex(0,
                  preferPosition: AutoScrollPosition.begin); // 以列表的上边框为准
              //   高亮
              _controller.highlight(0);
            },
            child: const Text("第1个")),
        ElevatedButton(
            onPressed: () async {
    
    
              // 滚动
              await _controller.scrollToIndex(19,
                  preferPosition: AutoScrollPosition.begin);
              //   高亮
              _controller.highlight(19);
            },
            child: const Text("第20个"))
      ], // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

在这里插入图片描述

高斯模糊

Stack(
        children: [
          Positioned(
              child: Image.network(
            'https://scpic2.chinaz.net/files/default/imgs/2023-07-24/f81ebfa03646059a_s.jpg',
            fit: BoxFit.fill,
            width: MediaQuery.of(context).size.width,
            height: MediaQuery.of(context).size.height,
          ),
          ),
        //  高斯模糊
          Center(
            child: SizedBox(
              width: 300,
              height: 300,
              child: ClipRRect(
              borderRadius: BorderRadius.circular(15.0),
                child: BackdropFilter(
                  // 设置视频和垂直方向的模糊程度
                  filter: ImageFilter.blur(sigmaX: 8.0,sigmaY: 8.0),
                  child: const Text("高斯模糊"),
                ),
              ),
            ),
          )
        ],
      )

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_41897680/article/details/131814404
今日推荐