flutter-dylive:基于flutter3.x+getx短视频直播|Flutter3仿抖音app

Flutter3-dyLive基于flutter3.19+dart3.3+getx+media_kit等技术构建仿抖音app实例。

使用技术

  • 编辑器:Vscode
  • 技术框架:Flutter3.19.2+Dart3.3.0
  • 路由/状态管理:get: ^4.6.6
  • 本地缓存:get_storage: ^2.1.1
  • 图片预览插件:photo_view: ^0.14.0
  • 刷新加载:easy_refresh^3.3.4
  • toast轻提示:toast^0.3.0
  • 视频套件:media_kit: ^1.1.10+1

FlutterDouYin实现了类似抖音整屏上下滑动视频、左右滑动切换页面模块,直播间进场/礼物动画,聊天等模块。

项目结构

项目中有些知识在之前分享的flutter3聊天实例中有介绍过,感兴趣的话可以去看看。

www.cnblogs.com/xiaoyan2017/p/1800...
www.cnblogs.com/xiaoyan2017/p/1804...

入口配置main.dart

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:media_kit/media_kit.dart';

import 'utils/index.dart';

// 引入布局模板
import 'layouts/index.dart';

import 'binding/binding.dart';

// 引入路由管理
import 'router/index.dart';

void main() async {
  // 初始化get_storage
  await GetStorage.init();

  // 初始化media_kit
  WidgetsFlutterBinding.ensureInitialized();
  MediaKit.ensureInitialized();

  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'FLUTTER3 DYLIVE',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFFFE2C55)),
        useMaterial3: true,
        // 修正windows端字体粗细不一致
        fontFamily: Platform.isWindows ? 'Microsoft YaHei' : null,
      ),
      home: const Layout(),
      // 全局绑定GetXController
      initialBinding: GlobalBindingController(),
      // 初始路由
      initialRoute: Utils.isLogin() ? '/' : '/login',
      // 路由页面
      getPages: routePages,
      // 错误路由
      // unknownRoute: GetPage(name: '/404', page: Error),
    );
  }
}

flutter3自定义底部凸起

flutter-dylive:基于flutter3.x+getx短视频直播|Flutter3仿抖音app

return Scaffold(
  backgroundColor: Colors.grey[50],
  body: pageList[pageCurrent],
  // 底部导航栏
  bottomNavigationBar: Theme(
    // Flutter去掉BottomNavigationBar底部导航栏的水波纹
    data: ThemeData(
      splashColor: Colors.transparent,
      highlightColor: Colors.transparent,
      hoverColor: Colors.transparent,
    ),
    child: Obx(() {
      return Stack(
        children: [
          Container(
            decoration: const BoxDecoration(
              border: Border(top: BorderSide(color: Colors.black54, width: .1)),
            ),
            child: BottomNavigationBar(
              backgroundColor: bottomNavigationBgcolor(),
              fixedColor: FStyle.primaryColor,
              unselectedItemColor: bottomNavigationItemcolor(),
              type: BottomNavigationBarType.fixed,
              elevation: 1.0,
              unselectedFontSize: 12.0,
              selectedFontSize: 12.0,
              currentIndex: pageCurrent,
              items: [
                ...pageItems
              ],
              onTap: (index) {
                setState(() {
                  pageCurrent = index;
                });
              },
            ),
          ),
          // 自定义底部导航栏中间按钮
          Positioned(
            left: MediaQuery.of(context).size.width / 2 - 15,
            top: 0,
            bottom: 0,
            child: InkWell(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  // Icon(Icons.tiktok, color: bottomNavigationItemcolor(centerDocked: true), size: 32.0,),
                  Image.asset('assets/images/applogo.png', width: 32.0, fit: BoxFit.contain,)
                  // Text('直播', style: TextStyle(color: bottomNavigationItemcolor(centerDocked: true), fontSize: 12.0),)
                ],
              ),
              onTap: () {
                setState(() {
                  pageCurrent = 2;
                });
              },
            ),
          ),
        ],
      );
    }),
  ),
);

flutter3仿抖音滑动

flutter-dylive:基于flutter3.x+getx短视频直播|Flutter3仿抖音app

flutter-dylive:基于flutter3.x+getx短视频直播|Flutter3仿抖音app

使用TabBarPageView联动滑动效果。

return Scaffold(
  extendBodyBehindAppBar: true,
  appBar: AppBar(
    forceMaterialTransparency: true,
    backgroundColor: [1, 2, 3].contains(pageVideoController.pageVideoTabIndex.value) ? null : Colors.transparent,
    foregroundColor: [1, 2, 3].contains(pageVideoController.pageVideoTabIndex.value) ? Colors.black : Colors.white,
    titleSpacing: 1.0,
    leading: Obx(() => IconButton(icon: Icon(Icons.menu, color: tabColor(),), onPressed: () {},),),
    title: Obx(() {
      return TabBar(
        controller: tabController,
        tabs: pageTabs.map((v) => Tab(text: v)).toList(),
        isScrollable: true,
        tabAlignment: TabAlignment.center,
        overlayColor: MaterialStateProperty.all(Colors.transparent),
        unselectedLabelColor: unselectedTabColor(),
        labelColor: tabColor(),
        indicatorColor: tabColor(),
        indicatorSize: TabBarIndicatorSize.label,
        unselectedLabelStyle: const TextStyle(fontSize: 16.0, fontFamily: 'Microsoft YaHei'),
        labelStyle: const TextStyle(fontSize: 16.0, fontFamily: 'Microsoft YaHei', fontWeight: FontWeight.w600),
        dividerHeight: 0,
        labelPadding: const EdgeInsets.symmetric(horizontal: 10.0),
        indicatorPadding: const EdgeInsets.symmetric(horizontal: 5.0),
        onTap: (index) {
          pageVideoController.updatePageVideoTabIndex(index); // 更新索引
          pageController.jumpToPage(index);
        },
      );
    }),
    actions: [
      Obx(() => IconButton(icon: Icon(Icons.search, color: tabColor(),), onPressed: () {},),),
    ],
  ),
  body: Column(
    children: [
      Expanded(
        child: Stack(
          children: [
            /// 水平滚动模块
            PageView(
              // 自定义滚动行为(支持桌面端滑动、去掉滚动条槽)
              scrollBehavior: PageScrollBehavior().copyWith(scrollbars: false),
              scrollDirection: Axis.horizontal,
              controller: pageController,
              onPageChanged: (index) {
                pageVideoController.updatePageVideoTabIndex(index); // 更新索引
                setState(() {
                  tabController.animateTo(index);
                });
              },
              children: [
                ...pageModules
              ],
            ),
          ],
        ),
      ),
    ],
  ),
);

flutter3视频拖拽进度条

flutter-dylive:基于flutter3.x+getx短视频直播|Flutter3仿抖音app

// flutter滑动短视频模块  Q:282310962

return Container(
  color: Colors.black,
  child: Column(
    children: [
      Expanded(
        child: Stack(
          children: [
            /// 垂直滚动模块
            PageView.builder(
              // 自定义滚动行为(支持桌面端滑动、去掉滚动条槽)
              scrollBehavior: PageScrollBehavior().copyWith(scrollbars: false),
              scrollDirection: Axis.vertical,
              controller: pageController,
              onPageChanged: (index) async {
                ...
              },
              itemCount: videoList.length,
              itemBuilder: (context, index) {
                return Stack(
                  children: [
                    // 视频区域
                    Positioned(
                      top: 0,
                      left: 0,
                      right: 0,
                      bottom: 0,
                      child: GestureDetector(
                        child: Stack(
                          children: [
                            // 短视频插件
                            Visibility(
                              visible: videoIndex == index,
                              child: Video(
                                controller: videoController,
                                fit: BoxFit.cover,
                                // 无控制条
                                controls: NoVideoControls,
                              ),
                            ),
                            // 播放/暂停按钮
                            StreamBuilder(
                              stream: player.stream.playing,
                              builder: (context, playing) {
                                return Visibility(
                                  visible: playing.data == false,
                                  child: Center(
                                    child: IconButton(
                                      padding: EdgeInsets.zero,
                                      onPressed: () {
                                        player.playOrPause();
                                      },
                                      icon: Icon(
                                        playing.data == true ? Icons.pause : Icons.play_arrow_rounded,
                                        color: Colors.white70,
                                        size: 70,
                                      ),
                                    ),
                                  ),
                                );
                              },
                            ),
                          ],
                        ),
                        onTap: () {
                          player.playOrPause();
                        },
                      ),
                    ),
                    // 右侧操作栏
                    Positioned(
                      bottom: 15.0,
                      right: 10.0,
                      child: Column(
                        ...
                      ),
                    ),
                    // 底部信息区域
                    Positioned(
                      bottom: 15.0,
                      left: 10.0,
                      right: 80.0,
                      child: Column(
                        ...
                      ),
                    ),
                    // 播放mini进度条
                    Positioned(
                      bottom: 0.0,
                      left: 10.0,
                      right: 10.0,
                      child: Visibility(
                        visible: videoIndex == index && position > Duration.zero,
                        child: Listener(
                          child: SliderTheme(
                            data: const SliderThemeData(
                              trackHeight: 2.0,
                              thumbShape: RoundSliderThumbShape(enabledThumbRadius: 4.0), // 调整滑块的大小
                              // trackShape: RectangularSliderTrackShape(), // 使用矩形轨道形状
                              overlayShape: RoundSliderOverlayShape(overlayRadius: 0), // 去掉Slider默认上下边距间隙
                              inactiveTrackColor: Colors.white24, // 设置非活动进度条的颜色
                              activeTrackColor: Colors.white, // 设置活动进度条的颜色
                              thumbColor: Colors.pinkAccent, // 设置滑块的颜色
                              overlayColor: Colors.transparent, // 设置滑块覆盖层的颜色
                            ),
                            child: Slider(
                              value: sliderValue,
                              onChanged: (value) async {
                                // debugPrint('当前视频播放时间$value');
                                setState(() {
                                  sliderValue = value;
                                });
                                // 跳转播放时间
                                await player.seek(duration * value.clamp(0.0, 1.0));
                              },
                              onChangeEnd: (value) async {
                                setState(() {
                                  sliderDraging = false;
                                });
                                // 继续播放
                                if(!player.state.playing) {
                                  await player.play();
                                }
                              },
                            ),
                          ),
                          onPointerMove: (e) {
                            setState(() {
                              sliderDraging = true;
                            });
                          },
                        ),
                      ),
                    ),
                    // 视频播放时间
                    Positioned(
                      bottom: 90.0,
                      left: 10.0,
                      right: 10.0,
                      child: Visibility(
                        visible: sliderDraging,
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: [
                            Text(position.label(reference: duration), style: const TextStyle(color: Colors.white, fontSize: 16.0, fontWeight: FontWeight.w600),),
                            Container(
                              margin: const EdgeInsets.symmetric(horizontal: 7.0),
                              child: const Text('/', style: TextStyle(color: Colors.white54, fontSize: 10.0,),),
                            ),
                            Text(duration.label(reference: duration), style: const TextStyle(color: Colors.white54, fontSize: 16.0, fontWeight: FontWeight.w600),),
                          ],
                        ),
                      ),
                    ),
                  ],
                );
              },
            ),
            /// 固定层
            // 红包
            Positioned(
              left: 15.0,
              top: MediaQuery.of(context).padding.top + 20,
              child: Container(
                height: 40.0,
                width: 40.0,
                decoration: BoxDecoration(
                  color: Colors.black12,
                  borderRadius: BorderRadius.circular(100.0),
                ),
                child: UnconstrainedBox(
                  child: Image.asset('assets/images/time-hb.png', width: 30.0, fit: BoxFit.contain,),
                ),
              ),
            ),
          ],
        ),
      ),
    ],
  ),
);

flutter3直播功能

flutter-dylive:基于flutter3.x+getx短视频直播|Flutter3仿抖音app

flutter-dylive:基于flutter3.x+getx短视频直播|Flutter3仿抖音app

// 商品购买动效
Container(
  ...
),

// 加入直播间动效
const AnimationLiveJoin(
  joinQueryList: [
    {'avatar': 'assets/images/logo.png', 'name': 'andy'},
    {'avatar': 'assets/images/logo.png', 'name': 'jack'},
    {'avatar': 'assets/images/logo.png', 'name': '一条咸鱼'},
    {'avatar': 'assets/images/logo.png', 'name': '四季平安'},
    {'avatar': 'assets/images/logo.png', 'name': '叶子'},
  ],
),

// 送礼物动效
const AnimationLiveGift(
  giftQueryList: [
    {'label': '小心心', 'gift': 'assets/images/gift/gift1.png', 'user': 'Jack', 'avatar': 'assets/images/avatar/uimg2.jpg', 'num': 12},
    {'label': '棒棒糖', 'gift': 'assets/images/gift/gift2.png', 'user': 'Andy', 'avatar': 'assets/images/avatar/uimg6.jpg', 'num': 36},
    {'label': '大啤酒', 'gift': 'assets/images/gift/gift3.png', 'user': '一条咸鱼', 'avatar': 'assets/images/avatar/uimg1.jpg', 'num': 162},
    {'label': '人气票', 'gift': 'assets/images/gift/gift4.png', 'user': 'Flower', 'avatar': 'assets/images/avatar/uimg5.jpg', 'num': 57},
    {'label': '鲜花', 'gift': 'assets/images/gift/gift5.png', 'user': '四季平安', 'avatar': 'assets/images/avatar/uimg3.jpg', 'num': 6},
    {'label': '捏捏小脸', 'gift': 'assets/images/gift/gift6.png', 'user': 'Alice', 'avatar': 'assets/images/avatar/uimg4.jpg', 'num': 28},
    {'label': '你真好看', 'gift': 'assets/images/gift/gift7.png', 'user': '叶子', 'avatar': 'assets/images/avatar/uimg7.jpg', 'num': 95},
    {'label': '亲吻', 'gift': 'assets/images/gift/gift8.png', 'user': 'YOYO', 'avatar': 'assets/images/avatar/uimg8.jpg', 'num': 11},
    {'label': '玫瑰', 'gift': 'assets/images/gift/gift12.png', 'user': '宇辉', 'avatar': 'assets/images/avatar/uimg9.jpg', 'num': 3},
    {'label': '私人飞机', 'gift': 'assets/images/gift/gift16.png', 'user': 'Hison', 'avatar': 'assets/images/avatar/uimg10.jpg', 'num': 273},
  ],
),

// 直播弹幕+商品讲解
Container(
  margin: const EdgeInsets.only(top: 7.0),
  height: 200.0,
  child: Row(
    crossAxisAlignment: CrossAxisAlignment.end,
    children: [
      Expanded(
        child: ListView.builder(
          padding: EdgeInsets.zero,
          itemCount: liveJson[index]['message']?.length,
          itemBuilder: (context, i) => danmuList(liveJson[index]['message'])[i],
        ),
      ),
      SizedBox(
        width: isVisibleGoodsTalk ? 7 : 35,
      ),
      // 商品讲解
      Visibility(
        visible: isVisibleGoodsTalk,
        child: Column(
          ...
        ),
      ),
    ],
  ),
),

// 底部工具栏
Container(
  margin: const EdgeInsets.only(top: 7.0),
  child: Row(
    ...
  ),
),

使用 SlideTransition 组件实现直播进场动画。

return SlideTransition(
  position: animationFirst ? animation : animationMix,
  child: Container(
    alignment: Alignment.centerLeft,
    margin: const EdgeInsets.only(top: 7.0),
    padding: const EdgeInsets.symmetric(horizontal: 7.0,),
    height: 23.0,
    width: 250,
    decoration: const BoxDecoration(
      gradient: LinearGradient(
        begin: Alignment.centerLeft,
        end: Alignment.centerRight,
        colors: [
          Color(0xFF6301FF), Colors.transparent
        ],
      ),
      borderRadius: BorderRadius.horizontal(left: Radius.circular(10.0)),
    ),
    child: joinList!.isNotEmpty ? 
      Text('欢迎 ${joinList![0]['name']} 加入直播间', style: const TextStyle(color: Colors.white, fontSize: 14.0,),)
      :
      Container()
    ,
  ),
);
class _AnimationLiveJoinState extends State<AnimationLiveJoin> with TickerProviderStateMixin {
  // 动画控制器
  late AnimationController controller = AnimationController(
    vsync: this,
    duration: const Duration(milliseconds: 500), // 第一个动画持续时间
  );
  late AnimationController controllerMix = AnimationController(
    vsync: this,
    duration: const Duration(milliseconds: 1000), // 第二个动画持续时间
  );
  // 动画
  late Animation<Offset> animation = Tween(begin: const Offset(2.5, 0), end: const Offset(0, 0)).animate(controller);
  late Animation<Offset> animationMix = Tween(begin: const Offset(0, 0), end: const Offset(-2.5, 0)).animate(controllerMix);

  Timer? timer;
  // 是否第一个动画
  bool animationFirst = true;
  // 是否空闲
  bool idle = true;
  // 加入直播间数据列表
  List? joinList;

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

    joinList = widget.joinQueryList!.toList();

    runAnimation();
    animation.addListener(() {
      if(animation.status == AnimationStatus.forward) {
        debugPrint('第一个动画进行中');
        idle = false;
        setState(() {});
      }else if(animation.status == AnimationStatus.completed) {
        debugPrint('第一个动画结束');
        animationFirst = false;
        if(controllerMix.isCompleted || controllerMix.isDismissed) {
          timer = Timer(const Duration(seconds: 2), () {
            controllerMix.forward();
            debugPrint('第二个动画开始');
          });
        }
        setState(() {});
      }
    });
    animationMix.addListener(() {
      if(animationMix.status == AnimationStatus.forward) {
        setState(() {});
      }else if(animationMix.status == AnimationStatus.completed) {
        animationFirst = true;
        controller.reset();
        controllerMix.reset();
        if(joinList!.isNotEmpty) {
          joinList!.removeAt(0);
        }
        idle = true;
        // 执行下一个数据
        runAnimation();
        setState(() {});
      }
    });
  }

  void runAnimation() {
    if(joinList!.isNotEmpty) {
      // 空闲状态才能执行,防止添加数据播放状态混淆
      if(idle == true) {
        if(controller.isCompleted || controller.isDismissed) {
          setState(() {});
          timer = Timer(Duration.zero, () {
            controller.forward();
          });
        }
      }
    }
  }

  @override
  void dispose() {
    controller.dispose();
    controllerMix.dispose();
    timer?.cancel();
    super.dispose();
  }

}

综上就是flutter3+dart3+getx开发抖音实例的一些知识分享,希望对大家有所帮助。

博客:uniapp-welive:基于uni-app+vue3+pinia多端直播商城实例|uniapp仿抖...
博客:Electron-React-MacOs基于electron27+react18客户端os系统

本作品采用《CC 协议》,转载必须注明作者和本文链接
本文为原创文章,未经作者允许不得转载,欢迎大家一起交流 QQ(282310962) wx(xy190310)
讨论数量: 1

该项目已经同步上线到工房,有兴趣的可以去看看哈。 :kissing_heart:

gf.bilibili.com/item/detail/110554...

1个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
文章
47
粉丝
45
喜欢
101
收藏
54
排名:367
访问:2.6 万
私信
所有博文
博客标签
react
1
angular
1
react仿微信
2
react实战开发
2
react+redux
1
react网页聊天
2
angular仿微信
1
angular聊天室
1
angular+node聊天
1
h5仿微信
1
仿微信语音
1
仿微信界面
1
RN弹窗
1
react-native自定义弹窗
1
react-native弹窗
1
React Native
1
reactNative仿微信
1
RN仿微信聊天
1
ReactNative朋友圈
1
uniapp仿微信
1
uniapp聊天室
2
uniapp聊天App
1
uni-app+vue实例
1
uni-app即时通讯
1
uniapp直播
1
uni-app仿抖音
1
uniapp仿陌陌
1
uni-app小视频
1
taro仿微信
1
taro聊天室
1
taro仿微信界面
1
taro+react聊天APP
1
taro即时通讯
1
electron+vue
1
electron-vue仿微信
1
electron聊天
1
electron实例
1
flutter实例
1
flutter仿微信
1
flutter+dart聊天
1
flutter聊天室
1
flutter聊天界面
1
vue自定义弹窗
1
vue全局对话框
1
vue长按弹出框
1
vue右键弹层
1
nuxt对话框
1
vue仿微信弹窗
1
vue仿探探
1
vue仿Tinder
1
vue卡片堆叠
1
vue翻牌滑动
1
nuxt仿探探
1
nuxt聊天室
1
nuxt仿微信
1
nuxt即时聊天
1
vue+nuxt聊天实例
1
nuxt.js朋友圈
1
vue.js自定义对话框
1
vue pc端弹窗
1
vue.js桌面端模态框
1
vue弹窗组件
1
vue仿layer
1
vue.js自定义滚动条
1
vue虚拟滚动条
1
vue美化滚动条
1
vue仿饿了么滚动条
1
Vue-Scrollbar
1
react.js弹窗示例
1
react桌面端弹框
1
react.js自定义对话框
1
react pc自定义弹窗
1
react全局弹框
1
vue3.0自定义组件
1
vue3弹框
1
vue3.x对话框
1
vue3.0弹窗
1
vue3.0实例
1
vue3.0聊天室
1
vue3.0仿微信
2
vue3聊天模板
1
vue3+vant3实战开发
1
vue3.x案例
1
vue3聊天实例
1
vue3.0仿QQ
1
vue3.x实战聊天
1
vue3网页聊天
1
vue3.0仿抖音app
1
vue3短视频
1
vue3.x小视频
1
vue3.0直播实例
1
vue3+vite2+vant3实战
1
vue3跨端开发
1
electron仿QQ
1
electron打包
1
electron聊天室
1
electron仿微信
1
electron仿抖音
1
electron短视频
1
electron直播
1
vite2+electron12
1
vite2+vue3.x+swiper
1
vue3+vite.js+vant3
1
vue3后台系统
1
Electron管理系统
1
vite2+electron后台
1
electron12权限管理
1
electron桌面端后台
1
vue3桌面管理
1
vite2+electron13
1
electron仿mac桌面
1
electron桌面管理
1
vite2桌面应用
1
uniapp短视频
1
uniapp仿抖音
1
uni-app直播
1
uniapp后台
1
uni-app+uview后台系统
1
svelte.js实战开发
1
svelte3仿微信
1
svelte+svelteKit聊天室
1
svelte聊天实例
2
svelte朋友圈
1
svelte.js仿微信
1
svelte.js网页聊天
1
svelte-ui-admin
1
svelte-admin后台管理
1
svelte管理系统
1
tauri桌面应用
1
tauri+vue3
1
vite3+tauri
1
tauri聊天程序
1
tauri仿微信
1
vue3后台管理
1
vite.js管理系统
1
vue3+vite4
1
vite4+pinia
1
vue3+pinia2
1
vue3-chatgpt
2
vite-chatgpt
1
chatgpt-mobile
1
electron-chatgpt
1
electron+vite4+vue3
1
electron25-vue3
1
chatgpt-vite
1
uni-chatgpt
1
uniapp+vue3+pinia
2
vite+uniapp
1
chatgpt-uniapp
1
tauri-admin
1
tauri+rust+vue3
1
tauri后台管理
1
tauri-vite
1
tauri+vue3桌面端后台
1
react18 hooks
2
react18+arco
2
react18+zustand
1
react18-webchat
1
react18-admin
1
react-arco-admin
1
react-vite-admin
1
react18后台管理
1
electron-mateos
1
electron27+react18
1
electron-react-macos
1
electron桌面os
1
react-macos
1
uniapp+vue3直播
1
flutter3-chat
2
flutter3仿微信
2
flutter3聊天
2
flutter3+dart3
1
flutter3桌面端开发
1
flutter3-douyin
1
flutter3仿抖音
1
flutter3短视频
1
flutter3直播
1
flutter3-macos
1
flutter3-osx
1
flutter3桌面os
1
flutter3仿macOS
1
社区赞助商