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

前言

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

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

感兴趣的可以看一下大佬的文章:Flutter完整开发实战详解系列,GSY Flutter 系列专栏整合

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

布局切换动画

动画效果


原文地址

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

代码

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

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

  @override
  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,
    );
  }

  @override
  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);

  @override
  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,
  });
}

使用

dart 复制代码
YcHomeBody(width: 240, height: 300,)

列表滑动监听

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

  @override
  void initState() {
    super.initState();

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

  @override
  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

安装

dart 复制代码
flutter pub add scroll_to_index
dart 复制代码
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));

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

  @override
  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.
    );
  }
}

高斯模糊

dart 复制代码
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("高斯模糊"),
                ),
              ),
            ),
          )
        ],
      )
相关推荐
火柴就是我6 小时前
flutter 之真手势冲突处理
android·flutter
Speed1236 小时前
`mockito` 的核心“打桩”规则
flutter·dart
法的空间6 小时前
Flutter JsonToDart 支持 JsonSchema
android·flutter·ios
恋猫de小郭6 小时前
Android 将强制应用使用主题图标,你怎么看?
android·前端·flutter
玲珑Felone7 小时前
从flutter源码看其渲染机制
android·flutter
ALLIN1 天前
Flutter 三种方式实现页面切换后保持原页面状态
flutter
Dabei1 天前
Flutter 国际化
flutter
Dabei1 天前
Flutter MQTT 通信文档
flutter
Dabei1 天前
Flutter 中实现 TCP 通信
flutter
孤鸿玉1 天前
ios flutter_echarts 不在当前屏幕 白屏修复
flutter