提示:
作者,gitHub账号:mjl0602开源
最终效果准备环境 Mac电脑 20G左右空间(xCode+Flutter SDK等) 由于在国内访问Flutter有时可能会受到限制,Flutter官方为中国开发者搭建了临时镜像,大家可以将如下环境变量加入到用户环境变量中:
export PUB_HOSTED_URL=https://pub.flutter-io.cn export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn下载SDK 下载SDK 然后依次执行一下命令
cd Desktop/ mkdir development cd development unzip ~/Downloads/flutter_macos_1.17.1-stable.zip然后添加Flutter工具到path中
export PATH=`pwd`/flutter/bin:$PATH运行 flutter doctor 运行以下命令查看是否需要安装其它依赖项来完成安装:(这里请你一定要记住+sudo)
sudo flutter doctor这个命令执行后需要等待一段时间,请你千万不要停止,虽然它有锁,但是不要那样做 如果执行后没有反应,请你用活动监视器杀掉git进程,然后就开启翻墙,这样就好了 更新环境变量
export PUB_HOSTED_URL=https://pub.flutter-io.cn //国内用户需要设置 export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn //国内用户需要设置 export PATH=PATH_TO_FLUTTER_GIT_DIRECTORY/flutter/bin:$PATH注意:PATH_TO_FLUTTER_GIT_DIRECTORY 为你flutter的路径,比如“~/document/code”,我的安装目录是 ~/Desktop/development
export PATH=~/document/code/flutter/bin:$PATH
特别提示: xCode版本过高,voip的 IOS 唤醒是会有问题的,建议安装低版本的Flutter sdk.
更多的环境问题参考:Flutter环境搭建 启动模拟器open -a Simulator
启动应用
flutter run项目大致讲解 入口文件 lib/main.dart
/// 自定义报错页面 if (kReleaseMode) { ErrorWidget.builder = (FlutterErrorDetails flutterErrorDetails) { debugPrint(flutterErrorDetails.toString()); return Material( child: Center( child: Text( "发生了没有处理的错误\n请通知开发者", textAlign: TextAlign.center, )), ); }; } runApp(MyApp()); }入门文件加载启动 APP
// This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Tiktok', theme: ThemeData( brightness: Brightness.dark, hintColor: Colors.white, accentColor: Colors.white, primaryColor: ColorPlate.orange, primaryColorBrightness: Brightness.dark, scaffoldBackgroundColor: ColorPlate.back1, dialogBackgroundColor: ColorPlate.back2, accentColorBrightness: Brightness.light, textTheme: TextTheme( bodyText1: StandardTextStyle.normal, ), ), home: HomePage(), ); } }顶部引入 homePage
import 'package:flutter_tiktok/pages/homePage.dart';homepage.dart :
import 'package:flutter_tiktok/pages/cameraPage.dart'; import 'package:flutter_tiktok/pages/followPage.dart'; import 'package:flutter_tiktok/pages/searchPage.dart'; import 'package:flutter_tiktok/pages/userPage.dart'; import 'package:flutter_tiktok/views/tikTokCommentBottomSheet.dart'; import 'package:flutter_tiktok/views/tikTokHeader.dart'; import 'package:flutter_tiktok/views/tikTokScaffold.dart'; import 'package:flutter_tiktok/views/tikTokVideo.dart'; import 'package:flutter_tiktok/views/tikTokVideoButtonColumn.dart'; import 'package:flutter_tiktok/views/tikTokVideoPlayer.dart'; import 'package:flutter_tiktok/views/tiktokTabBar.dart'; import 'package:fijkplayer/fijkplayer.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:safemap/safemap.dart'; import 'msgPage.dart'; /// 单独修改了bottomSheet组件的高度 import 'package:flutter_tiktok/other/bottomSheet.dart' as CustomBottomSheet; class HomePage extends StatefulWidget { @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State<HomePage> with WidgetsBindingObserver { TikTokPageTag tabBarType = TikTokPageTag.home; TikTokScaffoldController tkController = TikTokScaffoldController(); PageController _pageController = PageController(); VideoListController _videoListController = VideoListController(); /// 记录点赞 Map<int, bool> favoriteMap = {}; List<UserVideo> videoDataList = []; @override void didChangeAppLifecycleState(AppLifecycleState state) async { if (state != AppLifecycleState.resumed) { _videoListController.currentPlayer.pause(); } } @override void dispose() { WidgetsBinding.instance.removeObserver(this); _videoListController.currentPlayer.pause(); super.dispose(); } @override void initState() { videoDataList = UserVideo.fetchVideo(); WidgetsBinding.instance.addObserver(this); _videoListController.init( _pageController, videoDataList, ); tkController.addListener( () { if (tkController.value == TikTokPagePositon.middle) { _videoListController.currentPlayer.start(); } else { _videoListController.currentPlayer.pause(); } }, ); super.initState(); } @override Widget build(BuildContext context) { Widget currentPage; switch (tabBarType) { case TikTokPageTag.home: break; case TikTokPageTag.follow: currentPage = FollowPage(); break; case TikTokPageTag.msg: currentPage = MsgPage(); break; case TikTokPageTag.me: currentPage = UserPageSwitchPage(isSelfPage: true); break; } double a = MediaQuery.of(context).size.aspectRatio; bool hasBottomPadding = a < 0.55; bool hasBackground = hasBottomPadding; hasBackground = tabBarType != TikTokPageTag.home; if (hasBottomPadding) { hasBackground = true; } Widget tikTokTabBar = TikTokTabBar( hasBackground: hasBackground, current: tabBarType, onTabSwitch: (type) async { setState(() { tabBarType = type; if (type == TikTokPageTag.home) { _videoListController.currentPlayer.start(); } else { _videoListController.currentPlayer.pause(); } }); }, onAddButton: () { Navigator.of(context).push( MaterialPageRoute( fullscreenDialog: true, builder: (context) => CameraPage(), ), ); }, ); var userPage = UserPageSwitchPage( isSelfPage: false, canPop: true, onPop: () { tkController.animateToMiddle(); }, ); var searchPage = SearchPage( onPop: tkController.animateToMiddle, ); var header = tabBarType == TikTokPageTag.home ? TikTokHeader( onSearch: () { tkController.animateToLeft(); }, ) : Container(); // 组合 return TikTokScaffold( controller: tkController, hasBottomPadding: hasBackground, tabBar: tikTokTabBar, header: header, leftPage: searchPage, rightPage: userPage, enableGesture: tabBarType == TikTokPageTag.home, // onPullDownRefresh: _fetchData, page: Stack( // index: currentPage == null ? 0 : 1, children: <Widget>[ PageView.builder( key: Key('home'), controller: _pageController, pageSnapping: true, physics: ClampingScrollPhysics(), scrollDirection: Axis.vertical, itemCount: _videoListController.videoCount, itemBuilder: (context, i) { // 拼一个视频组件出来 var data = videoDataList[i]; bool isF = SafeMap(favoriteMap)[i].boolean ?? false; var player = _videoListController.playerOfIndex(i); // 右侧按钮列 Widget buttons = TikTokButtonColumn( isFavorite: isF, onAvatar: () { tkController.animateToPage(TikTokPagePositon.right); }, onFavorite: () { setState(() { favoriteMap[i] = !isF; }); // showAboutDialog(context: context); }, onComment: () { CustomBottomSheet.showModalBottomSheet( backgroundColor: Colors.white.withOpacity(0), context: context, builder: (BuildContext context) => TikTokCommentBottomSheet(), ); }, onShare: () {}, ); // video Widget currentVideo = Center( child: FijkView( player: player, color: Colors.black, panelBuilder: (_, __, ___, ____, _____) => Container(), ), ); currentVideo = TikTokVideoPage( hidePauseIcon: player.state != FijkState.paused, aspectRatio: 9 / 16.0, key: Key(data.url + '$i'), tag: data.url, bottomPadding: hasBottomPadding ? 16.0 : 16.0, userInfoWidget: VideoUserInfo( desc: data.desc, bottomPadding: hasBottomPadding ? 16.0 : 50.0, // onGoodGift: () => showDialog( // context: context, // builder: (_) => FreeGiftDialog(), // ), ), onSingleTap: () async { if (player.state == FijkState.started) { await player.pause(); } else { await player.start(); } setState(() {}); }, onAddFavorite: () { setState(() { favoriteMap[i] = true; }); }, rightButtonColumn: buttons, video: currentVideo, ); return currentVideo; }, ), Opacity( opacity: 1, child: currentPage ?? Container(), ), // Center( // child: Text(_currentIndex.toString()), // ) ], ), ); } }实现的功能 上下刷视频,视频会自动加载封面 左右滑动去搜索与个人中心 双击爱心点赞 看评论 切换底部Tabbar 应用截图
查看更多关于Flutter高仿抖音,源码开源 [附教程]的详细内容...
声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://haodehen.cn/did129368