Flutter最酷炫瀑布流实现

Posted 怀君

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter最酷炫瀑布流实现相关的知识,希望对你有一定的参考价值。

  • 背景

公司APP首页列表有此需求

  • 使用哪些组件

StaggeredGridView.countBuilder

  • 如何实现,难点在哪里

瀑布流的宽度与高度。
宽度=(屏幕宽—组件间隙) / 2
高度=图片高度进行设置
这样才能实现并展示瀑布流的效果

在pubspec.yaml 文件 添加 flutter_staggered_grid_view: ^0.4.1
flutter_screenutil: ^5.0.0这个是屏幕适配使用
使用过程中有什么问题可以自己私信我

  • 最终效果图
    给你们配置了网红孙一宁

FeedListView.dart文件

class FeedListView extends StatefulWidget 
  final FeedMode? mode;

  FeedListView(Key? key, this.mode) : super(key: key);

  @override
  FeedListViewState createState() => FeedListViewState();


class FeedListViewState extends State<FeedListView> 
  List<String> imgList = [
    'https://bkimg.cdn.bcebos.com/pic/500fd9f9d72a6059252d1977427a239b033b5bb50c05?x-bce-process=image/watermark,image_d2F0ZXIvYmFpa2UxNTA=,g_7,xp_5,yp_5/format,f_auto',
    'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fnimg.ws.126.net%2F%3Furl%3Dhttp%3A%2F%2Fdingyue.ws.126.net%2F2021%2F0624%2Fca3f3547j00qv6yuq002ic000mn0148c.jpg%26thumbnail%3D650x2147483647%26quality%3D80%26type%3Djpg&refer=http%3A%2F%2Fnimg.ws.126.net&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1636620826&t=1f7e60c5d4a5baadafbae5d25dfdae39',
    'https://bkimg.cdn.bcebos.com/pic/0d338744ebf81a4c510f10a4bd647759252dd42a0805?x-bce-process=image/watermark,image_d2F0ZXIvYmFpa2UyMjA=,g_7,xp_5,yp_5/format,f_auto',
    'https://upload-images.jianshu.io/upload_images/6865547-100e574111cf0696.png?imageMogr2/auto-orient/strip|imageView2/2/w/507/format/webp',
    'https://bkimg.cdn.bcebos.com/pic/500fd9f9d72a6059252d1977427a239b033b5bb50c05?x-bce-process=image/watermark,image_d2F0ZXIvYmFpa2UxNTA=,g_7,xp_5,yp_5/format,f_auto',
    'https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/76a788337365419c971a94a5fbdbc4f8~tplv-k3u1fbpfcp-zoom-crop-mark:1304:1304:1304:734.awebp?',
    'https://upload-images.jianshu.io/upload_images/6865547-100e574111cf0696.png?imageMogr2/auto-orient/strip|imageView2/2/w/507/format/webp',
    'https://bkimg.cdn.bcebos.com/pic/0ff41bd5ad6eddc451dad5965395a1fd5266d0169502?x-bce-process=image/watermark,image_d2F0ZXIvYmFpa2UxMTY=,g_7,xp_5,yp_5/format,f_auto',
    'https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/76a788337365419c971a94a5fbdbc4f8~tplv-k3u1fbpfcp-zoom-crop-mark:1304:1304:1304:734.awebp?',
    'https://upload-images.jianshu.io/upload_images/6865547-100e574111cf0696.png?imageMogr2/auto-orient/strip|imageView2/2/w/507/format/webp',
    'https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/76a788337365419c971a94a5fbdbc4f8~tplv-k3u1fbpfcp-zoom-crop-mark:1304:1304:1304:734.awebp?',
    'https://upload-images.jianshu.io/upload_images/6865547-100e574111cf0696.png?imageMogr2/auto-orient/strip|imageView2/2/w/507/format/webp',
  ];

  List<WaterfallsFlowModel> modelList = <WaterfallsFlowModel>[];
  
  @override
  void initState() 
    super.initState();
       var ii = new Random().nextInt(5);
    var i = 0;
    imgList.forEach((element) 
      var _random = new Random();
      int next(int min, int max) => min + _random.nextInt(max - min);
      var c = next(200, 800);
      i++;
      var model = WaterfallsFlowModel(
          imageUrl: element,
          content: i > ii ? '影片名称影片名称影片名称影片名称影片名称' : '影片名称',
          like: '2313',
          imageHeight: c);
      modelList.add(model);
    );
  

  @override
  Widget build(BuildContext context) 
    homeProvider = context.watch<HomeProvider>();
        return Container(
          child: StaggeredGridView.countBuilder(
              padding: EdgeInsets.symmetric(horizontal: 5.w),
              // shrinkWrap: true,
              crossAxisCount: 4,
              itemCount: provider.modelList.length,
              itemBuilder: (context, i) 
                if (i == 0 && mode == FeedMode.user) 
                  return UserInfoTop();
                
                return GestureDetector(
                  onTap: _onTap,
                  child: FeedWidget(
                    mode: mode,
                    model: provider.modelList[i],
                  ),
                );
              ,
              staggeredTileBuilder: (index) 
                  return StaggeredTile.fit(2);
              ),
        );
  

WaterfallsFlowModel.dart

class WaterfallsFlowModel 
  String? imageUrl;
  String? content;
  String? name;
  String? avatarUrl;
  String? like;
  bool? isLise;
  int? imageWidth;
  int? imageHeight;

  WaterfallsFlowModel(
      this.imageUrl,
      this.content,
      this.name,
      this.avatarUrl,
      this.like,
      this.isLise,
      this.imageWidth,
      this.imageHeight);


FeedWidget.dart 瀑布流Widget

///瀑布流组件
class FeedWidget extends StatefulWidget 
  final String? imageUrl;
  final WaterfallsFlowModel? model;
  final FeedMode? mode;

  FeedWidget(Key? key, this.imageUrl, this.model, this.mode)
      : super(key: key);

  @override
  _FeedWidgetState createState() => _FeedWidgetState();


class _FeedWidgetState extends State<FeedWidget> 
  var imageWidth = ScreenUtil().screenWidth - 30.w;
  var isMaxTwo = false;
  late WaterfallsFlowModel model;
  var isLike = false;

  @override
  Widget build(BuildContext context) 
    model = widget.model!;
    _didExceedOneMoreLines(model.content!, ScreenUtil().screenWidth / 2 - 120.w,
        TextStyle(fontSize: 30.sp));
    return Container(
      margin: EdgeInsets.fromLTRB(5.w, 5.w, 5.w, 5.w),
      height: isMaxTwo
          ? model.imageHeight!.w + 190.w
          : model.imageHeight!.w + 150.w,
      decoration: BoxDecoration(
          borderRadius: BorderRadius.all(
            Radius.circular(8.w),
          ),
          color: Colors.white,
          boxShadow: [
            BoxShadow(
              // 设置第一个阴影效果
              color: Color(0xfff2f2f4), // 阴影颜色为绿色
              blurRadius: 8.w, // 模糊半径为10
            ), // 延伸半径为10
          ]),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _imageWidget(),
          _movieWidget(),
          _likeWidget(),
        ],
      ),
    );
  

  ///1.顶部图片
  _imageWidget() 
    return Image.network(model.imageUrl!,
      width: imageWidth,
      height: model.imageHeight!.w,
    );
  

  ///2.影片名称
  _movieWidget() 
    return Padding(
      padding: EdgeInsets.fromLTRB(20.w, 20.w, 20.w, 0),
      child: Text(
        model.content!,
        overflow: TextOverflow.ellipsis,
        style: TextStyle(
          color: KColor.black,
          fontWeight: FontWeight.w700,
          fontSize: 30.sp,
        ),
        maxLines: 2,
      ),
    );
  

  ///3.用户名称与点赞数量
  _likeWidget() 
    return Padding(
      padding: EdgeInsets.all(20.w),
      child: Row(
        children: [
          widget.mode == FeedMode.user ? _viewsWidget() : _userInfoWidget(),
          SizedBox(width: 4.w),
          Text('111',
              overflow: TextOverflow.ellipsis,
              maxLines: 1,
              style: TextStyle(color: Color(0xFF66667E), fontSize: 24.sp))
        ],
      ),
    );
  

  ///3.1 用户信息
  _userInfoWidget() 
    return Expanded(
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          GestureDetector(
            onTap: () 
            ,
            child: Image.network(model.imageUrl!,
      width: imageWidth,
      height: model.imageHeight!.w,
    ),
          SizedBox(width: 12.w),
          Expanded(
            child: GestureDetector(
              onTap: () 
              ,
              child: Text(
                '用户名称用户名称用户名称',
                overflow: TextOverflow.ellipsis,
                style: TextStyle(
                  color: Color(0xFF66667E),
                  fontSize: 24.sp,
                ),
                maxLines: 1,
              ),
            ),
          ),
          SizedBox(width: 20.w),
        ],
      ),
    );
  

  ///3.2 浏览量
  _viewsWidget() 
    return Expanded(
      child: Row(
        children: [
          Text('111',
              overflow: TextOverflow.ellipsis,
              maxLines: 1,
              style: TextStyle(color: Color(0xFF66667E), fontSize: 24.sp))
        ],
      ),
    );
  

  _didExceedOneMoreLines(String text, double width, TextStyle style) 
    final span = TextSpan(text: text, style: style);
    final tp =
        TextPainter(text: span, maxLines: 1, textDirection: TextDirection.ltr);
    tp.layout(maxWidth: width);
    if (tp.didExceedMaxLines) 
      isMaxTwo = true;
     else 
      isMaxTwo = false;
    
  

以上是关于Flutter最酷炫瀑布流实现的主要内容,如果未能解决你的问题,请参考以下文章

Flutter之瀑布流效果——Flutter基础系列

Flutter自适应瀑布流

Flutter瀑布流及通用列表解决方案

一「表」走天下,Flutter瀑布流及通用列表解决方案

HTML+CSS+JS实现 ❤️酷炫3D瀑布流动画特效❤️

flutter 布局时内容溢出容器的类似Overflow-hidden的简单解决