Flutter评论展示插件comment_sheet的使用

发布于 1周前 作者 nodeper 来自 Flutter

Flutter评论展示插件comment_sheet的使用

comment_sheet 是一个Flutter插件,它提供了一个可以拖动到其他位置的底部弹出表单(Sheet)。这个插件非常适合用于展示评论、聊天记录或其他需要交互的列表内容。下面是一个完整的示例demo,展示了如何使用 comment_sheet 插件。

示例Demo

Demo

使用方法

要使用 comment_sheet 插件,首先需要在 pubspec.yaml 文件中添加依赖:

dependencies:
  comment_sheet: ^最新版本号

然后在你的Dart文件中导入 comment_sheet 包:

import 'package:comment_sheet/comment_sheet.dart';

接下来,我们可以通过以下代码来创建一个包含评论功能的页面。

完整示例代码

import 'dart:math';

import 'package:comment_sheet/comment_sheet.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  [@override](/user/override)
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  // 创建一个CommentSheetController实例,用于控制Sheet的行为
  final CommentSheetController commentSheetController = CommentSheetController();
  ScrollController scrollController = ScrollController();

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Builder(builder: (context) {
          return CustomScrollView(
            physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
            slivers: [
              // 添加下拉刷新控件
              SliverFillRemaining(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    ListTile(
                      title: const Text("Open Sheet"),
                      onTap: () {
                        // 显示底部Sheet
                        showBottomSheet(
                          context: context,
                          backgroundColor: Colors.transparent,
                          builder: (context) {
                            return CommentSheet(
                              slivers: [
                                buildSliverList(), // 构建评论列表
                              ],
                              grabbingPosition: WidgetPosition.above, // 抓取区域的位置
                              initTopPosition: 200, // 初始化顶部位置
                              calculateTopPosition: calculateTopPosition, // 计算顶部位置的方法
                              scrollController: scrollController, // 滚动控制器
                              grabbing: Builder(builder: (context) {
                                return buildGrabbing(context); // 构建抓取区域
                              }),
                              topWidget: (info) {
                                return Positioned(
                                  top: 0,
                                  left: 0,
                                  right: 0,
                                  height: max(0, info.currentTop),
                                  child: const Placeholder(color: Colors.green), // 顶部占位符
                                );
                              },
                              topPosition: WidgetPosition.below, // 顶部Widget的位置
                              bottomWidget: buildBottomWidget(), // 底部Widget
                              onPointerUp: (
                                BuildContext context,
                                CommentSheetInfo info,
                              ) {
                                // 手指抬起时的回调
                              },
                              onAnimationComplete: (
                                BuildContext context,
                                CommentSheetInfo info,
                              ) {
                                // 动画完成时的回调
                                if (info.currentTop >= info.size.maxHeight - 100) {
                                  Navigator.of(context).pop(); // 如果Sheet滑动到顶部,则关闭
                                }
                              },
                              commentSheetController: commentSheetController, // 控制器
                              onTopChanged: (top) {
                                // 顶部位置变化时的回调
                              },
                              child: const Placeholder(), // Sheet内部的子Widget
                              backgroundBuilder: (context) {
                                return Container(
                                  color: const Color(0xFF0F0F0F), // 背景颜色
                                  margin: const EdgeInsets.only(top: 20), // 背景边距
                                );
                              },
                            );
                          },
                        );
                      },
                    ),
                    ListTile(
                      title: const Text("Show Sheet in Stack"),
                      onTap: () {
                        // 导航到另一个页面,展示Sheet在Stack中
                        Navigator.push(
                          context,
                          MaterialPageRoute(
                            builder: (context) {
                              return const ScreenSheetDemo();
                            },
                          ),
                        );
                      },
                    ),
                  ],
                ),
              )
            ],
          );
        }),
      ),
    );
  }

  // 构建底部Widget
  Container buildBottomWidget() {
    return Container(
      color: Colors.transparent,
      height: 50,
      child: const Placeholder(color: Colors.blue),
    );
  }

  // 计算顶部位置的逻辑
  double calculateTopPosition(CommentSheetInfo info) {
    final vy = info.velocity.getVelocity().pixelsPerSecond.dy;
    final top = info.currentTop;
    double p0 = 0;
    double p1 = 200;
    double p2 = info.size.maxHeight - 100;

    if (top > p1) {
      if (vy > 0) {
        if (info.isAnimating && info.animatingTarget == p1 && top < p1 + 10) {
          return p1;
        } else {
          return p2;
        }
      } else {
        return p1;
      }
    } else if (top == p1) {
      return p1;
    } else if (top == p0) {
      return p0;
    } else {
      if (vy > 0) {
        if (info.isAnimating && info.animatingTarget == p0 && top < p0 + 10) {
          return p0;
        } else {
          return p1;
        }
      } else {
        return p0;
      }
    }
  }

  // 构建抓取区域
  Widget buildGrabbing(BuildContext context) {
    return const GrabbingWidget();
  }

  // 构建评论列表
  Widget buildSliverList() {
    return SliverList(
      delegate: SliverChildBuilderDelegate((context, index) {
        return const ListItemWidget();
      }, childCount: 20),
    );
  }
}

// 抓取区域的Widget
class GrabbingWidget extends StatelessWidget {
  const GrabbingWidget({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Container(
      decoration: const BoxDecoration(
        color: Color(0xFF0F0F0F),
        borderRadius: BorderRadius.only(
          topLeft: Radius.circular(12),
          topRight: Radius.circular(12),
        ),
      ),
      padding: const EdgeInsets.only(top: 10),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Container(
            width: 40,
            height: 4,
            margin: const EdgeInsets.only(top: 4),
            decoration: BoxDecoration(
                color: Colors.white60,
                borderRadius: BorderRadius.circular(100)),
          ),
          Row(
            children: [
              const Padding(
                padding: EdgeInsets.only(left: 15),
                child: Text(
                  "Bình luận", // 评论标题
                  style: TextStyle(
                    fontWeight: FontWeight.w700,
                    color: Colors.white,
                    fontSize: 16,
                  ),
                ),
              ),
              const Padding(
                padding: EdgeInsets.only(left: 6.0),
                child: Text(
                  "48", // 评论数量
                  style: TextStyle(
                    fontWeight: FontWeight.w400,
                    color: Colors.white70,
                    fontSize: 13,
                  ),
                ),
              ),
              const Spacer(),
              const Icon(
                Icons.menu_sharp, // 菜单图标
                size: 26,
                color: Colors.white,
              ),
              Padding(
                padding: const EdgeInsets.only(left: 10.0, right: 10),
                child: IconButton(
                  onPressed: () {
                    Navigator.of(context).pop(); // 关闭Sheet
                  },
                  icon: const Icon(
                    Icons.close, // 关闭按钮
                    size: 26,
                    color: Colors.white,
                  ),
                ),
              )
            ],
          ),
          Container(
            color: const Color(0xFF292929),
            width: double.infinity,
            height: 1,
          ),
        ],
      ),
    );
  }
}

// 评论项的Widget
class ListItemWidget extends StatelessWidget {
  const ListItemWidget({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Material(
      color: const Color(0xFF0F0F0F),
      child: InkWell(
        onTap: () {
          Navigator.of(context).pop(); // 点击评论项时关闭Sheet
        },
        child: Container(
          width: double.infinity,
          padding: const EdgeInsets.only(top: 12, bottom: 0, left: 10, right: 10),
          child: Row(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Padding(
                padding: const EdgeInsets.only(left: 5, right: 10),
                child: ClipOval(
                  child: Image.network(
                    "https://yt3.ggpht.com/yti/AJo0G0kUnHqoybmWPJG4GNm0G-lfCiCPbEP62v5tq9PZsA=s48-c-k-c0x00ffffff-no-rj",
                    width: 25,
                    height: 25,
                  ),
                ),
              ),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text(
                      'Andrea Quintanilla * 3 tháng trước', // 评论者信息
                      style: TextStyle(
                          fontSize: 10,
                          fontWeight: FontWeight.w400,
                          color: Color(0xFFAEAEAE)),
                    ),
                    const Padding(
                      padding: EdgeInsets.only(top: 6, bottom: 12),
                      child: Text(
                        'Que buen trabajo, que buenos enganches, genial!!!!  MTV la tenes adentro, jajaja. Saludos cordiales desde Buenos Aires, Argentina, Argentina, Argentina!', // 评论内容
                        style: TextStyle(
                            fontSize: 13,
                            fontWeight: FontWeight.w400,
                            color: Color(0xFFF6F6F6)),
                      ),
                    ),
                    Row(
                      children: const [
                        Icon(
                          Icons.thumb_up_outlined, // 点赞图标
                          size: 15,
                          color: Colors.white,
                        ),
                        Padding(
                          padding: EdgeInsets.only(left: 16.0, right: 16.0),
                          child: Icon(
                            Icons.thumb_down_alt_outlined, // 点踩图标
                            size: 15,
                            color: Colors.white,
                          ),
                        ),
                        Icon(
                          Icons.comment_outlined, // 回复图标
                          size: 15,
                          color: Colors.white,
                        ),
                      ],
                    )
                  ],
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

更多关于Flutter评论展示插件comment_sheet的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter评论展示插件comment_sheet的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何使用Flutter的comment_sheet插件来展示评论的示例代码。comment_sheet是一个假设的插件名称,因为Flutter社区中并没有一个广泛使用的名为comment_sheet的官方插件。不过,我会根据常见的Flutter插件使用模式来模拟一个类似的实现。

假设我们有一个自定义的CommentSheet插件,它允许我们展示评论列表,并支持添加新评论。以下是如何在Flutter项目中集成和使用这个插件的示例代码。

1. 添加依赖

首先,我们需要在pubspec.yaml文件中添加这个假设的comment_sheet插件依赖(注意:这只是一个示例,你需要替换为实际存在的插件):

dependencies:
  flutter:
    sdk: flutter
  comment_sheet: ^0.1.0  # 假设的版本号

然后运行flutter pub get来安装依赖。

2. 使用插件

接下来,我们在main.dart文件中使用CommentSheet插件来展示评论。

import 'package:flutter/material.dart';
import 'package:comment_sheet/comment_sheet.dart';  // 假设的导入路径

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Comment Sheet Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: CommentScreen(),
    );
  }
}

class CommentScreen extends StatefulWidget {
  @override
  _CommentScreenState createState() => _CommentScreenState();
}

class _CommentScreenState extends State<CommentScreen> {
  List<String> comments = [
    "This is a great app!",
    "I love the new features!",
    "Keep up the good work!",
  ];

  final TextEditingController _controller = TextEditingController();

  void _addComment() {
    setState(() {
      if (_controller.text.isNotEmpty) {
        comments.add(_controller.text);
        _controller.clear();
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Comments'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Column(
          children: [
            Expanded(
              child: CommentSheet(
                comments: comments,
                onCommentPressed: () {}, // 假设的回调,可以处理点击评论的动作
              ),
            ),
            TextField(
              controller: _controller,
              decoration: InputDecoration(
                border: OutlineInputBorder(),
                labelText: 'Add a comment',
              ),
              onSubmitted: _addComment,
            ),
            ElevatedButton(
              onPressed: _addComment,
              child: Text('Post'),
            ),
          ],
        ),
      ),
    );
  }
}

// 假设的 CommentSheet 组件实现(实际上你需要从插件中获取)
class CommentSheet extends StatelessWidget {
  final List<String> comments;
  final VoidCallback onCommentPressed;

  CommentSheet({required this.comments, required this.onCommentPressed});

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: comments.length,
      itemBuilder: (context, index) {
        return ListTile(
          title: Text(comments[index]),
          onTap: onCommentPressed,
        );
      },
    );
  }
}

注意事项

  1. 插件实际存在性comment_sheet是一个假设的插件名称。在实际使用中,你需要找到一个真实存在的Flutter评论展示插件,或者自己实现一个。

  2. 插件功能:上述示例中的CommentSheet组件是一个简单的ListView,用于展示评论列表。实际插件可能包含更多功能,如评论点赞、回复、加载更多评论等。

  3. 错误处理:在实际应用中,你还需要添加错误处理逻辑,例如处理网络请求失败、评论添加失败等情况。

  4. 样式定制:你可以根据需要定制评论列表的样式,使其更符合你的应用风格。

  5. 数据持久化:如果评论数据需要持久化存储(例如保存到数据库或服务器),你需要添加相应的逻辑来处理数据的保存和加载。

回到顶部