6

第五篇- 抖音的强大对手来了,用Flutter手撸一个抖音国际版,看看有多炫 - 风清扬 No....

 2 years ago
source link: https://www.cnblogs.com/fengqingyangNo1/p/15988863.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

第五篇- 抖音的强大对手来了,用Flutter手撸一个抖音国际版,看看有多炫

由于中间几个月项目天天加班,导致没没时间更新,最近一段时间对前端进行了重构,加了很多页面,如登录、注册、关注、个人中心等,目前写这个纯属业余个人爱好,所以断断续续的继续在做......

前端地址:https://www.pgyer.com/dtok
后端服务器地址:http://47.95.209.198:8181/

注释:由于本人的apple id无法打包ios、所以暂时只打包的android版本,ios版本正在解决账号问题

效果如下:

之前技术采用flutter做的前端,后端api则对接的是抖音官方api,由于抖音的官方api更新频繁,导致经常播放不了,所以索性自己来写服务器后端api,那么后端api采用了那些技术咧

  • springcloud 主要是后台控制面板 演示地址:http://47.95.209.198:8181/login
  • elasticsearch 主要对视频数据离线查询
  • ipfs 用于分布式节点存储短视频
  • ethereum 用户激励用户存储短视频、毕竟买服务器存花费够大的
  • 支持国家化,多语言切换
  • ipfs上传、下载文件
  • 上下轮播时优化播放效果

其他功能还在继续完善,各位喜欢的话欢迎点个star 前端项目地址:https://github.com/telsacoin/telsavideo
后端需要的话请留下邮箱

本期最大的优化就是国际化,flutter国家化按以下步骤

在pubspec.yaml文件加上

  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  intl: ^0.17.0 # Add this line
  ffi: ^1.1.2

在底部的flutter设置里添加

# The following section is specific to Flutter.
flutter:
  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true
  generate: true # Add this line

新建多语言包

在lib目录新建子目录l10n
里面添加app_zh.arb文件
内容如下:

{
    "home_top_foryou":"推荐",
    "home_top_following":"关注",
    "home_share":"分享",
    "home_buttom_title":"首页",
    "home_buttom_discover":"发现",
    "home_buttom_notification":"通知",
    "home_buttom_persion":"我"
}

在main文件引用

import 'package:flutter_gen/gen_l10n/app_localizations.dart';

在build里加入多语言检测及支持的代码

return MaterialApp(
    debugShowCheckedModeBanner: false,
    onGenerateTitle: (context) =>
        AppLocalizations.of(context)!.home_buttom_title,
    home: SplashScreen(),
    localeResolutionCallback: (
      Locale? locale,
      Iterable<Locale> supportedLocales,
    ) {
      return locale;
    },
    localizationsDelegates: AppLocalizations.localizationsDelegates,
    supportedLocales: AppLocalizations.supportedLocales,
    theme: ThemeData(
      textSelectionTheme: TextSelectionThemeData(
        cursorColor: Colors.white,
      ),
      splashColor: Colors.transparent,
      highlightColor: Colors.transparent,
      primarySwatch: Colors.red,
      primaryColor: Colors.black,
      indicatorColor: Colors.white,
      tabBarTheme: TabBarTheme(),
    ),
    /* initialRoute: '/',
   onGenerateRoute: RouteGenerator.generateRoute, */
    builder: (context, child) {
      return ScrollConfiguration(
        behavior: MyBehavior(),
        child: child!,
      );
    },
  );

然后在需要引用的位置加入

import 'package:flutter_gen/gen_l10n/app_localizations.dart';

调用的位置

AppLocalizations.of(context)!.home_top_foryou

至此,国际化就完成了

另外本地针对播放模块进行了优化,将代码拆分到videoplayer.dart文件.一来是方便代码阅读,而来可以作为子组件使用,其他的代码写得太冗余也在继续拆开,独立出来,各位感兴趣的可以关注项目的进展。

采用FutureBuilder对界面请求数据异步处理,当加载完成后才播放,效果更佳

代码如下:

return FutureBuilder<DTok>(
    future: videos,
    builder: (context, snapshot) {
      print(snapshot.connectionState);
      if (snapshot.connectionState == ConnectionState.waiting) {
        return loading;
        // return Column(
        //   crossAxisAlignment: CrossAxisAlignment.center,
        //   mainAxisAlignment: MainAxisAlignment.center,
        //   children: [
        //     loading,
        //     Visibility(
        //       visible: snapshot.hasData,
        //       child: PageView.builder(
        //           controller: foryouController,
        //           onPageChanged: (index) {
        //             //when the video is changing, release the previous video instance.
        //             //disposeVideo();
        //             setState(() {});
        //           },
        //           scrollDirection: Axis.vertical,
        //           itemCount: snapshot.data!.itemList!.length,
        //           itemBuilder: (context, index) {
        //             var item = snapshot.data!.itemList![index];
        //             return Videoplayer(
        //               item: item,
        //               width: MediaQuery.of(context).size.width,
        //               heigth: MediaQuery.of(context).size.height,
        //             );
        //           }),
        //     )
        //   ],
        // );
      } else if (snapshot.connectionState == ConnectionState.done) {
        if (snapshot.hasError) {
          return Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Text('Error, Please restart your app agagin')
            ],
          );
        } else if (snapshot.hasData) {
          try {
            return PageView.builder(
                controller: foryouController,
                onPageChanged: (index) {
                  //when the video is changing, release the previous video instance.
                  //disposeVideo();
                  //setState(() {});
                },
                scrollDirection: Axis.vertical,
                itemCount: snapshot.data!.itemList!.length,
                itemBuilder: (context, index) {
                  var item = snapshot.data!.itemList![index];
                  return Videoplayer(
                    item: item,
                    width: MediaQuery.of(context).size.width,
                    heigth: MediaQuery.of(context).size.height,
                  );
                });
          } catch (e) {
            return Container(
              width: MediaQuery.of(context).size.width,
              height: MediaQuery.of(context).size.height,
              color: Colors.black,
              child: Center(
                  child: Text(
                'Error, Please restart your app again.',
                style: TextStyle(color: Colors.white),
              )),
            );
          }
        } else {
          // empty data
          return loading;
        }
      } else {
        return Text('State: ${snapshot.connectionState}');
      }
    });

这里可以看到当snapshot.connectionState == ConnectionState.waiting的时候请求的数据正在加载中,则显示加载的图标loading

当snapshot.connectionState == ConnectionState.done 时,此时数据已经加载完毕,但是加载完毕有可能也没有数据,所以需要判断不同的情况

当加载出现异常情况则显示异常的widget

if (snapshot.hasError) {
      return Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          const Text('Error, Please restart your app agagin')
        ],
      );
    } 

当if (snapshot.hasData)则说明有返回值,但是这个返回值不一定就是我们需要的数据,所以还需要try catch一下,保证呈现给用户的界面是正常的

try {
      return PageView.builder(
          controller: foryouController,
          onPageChanged: (index) {
            //when the video is changing, release the previous video instance.
            //disposeVideo();
            //setState(() {});
          },
          scrollDirection: Axis.vertical,
          itemCount: snapshot.data!.itemList!.length,
          itemBuilder: (context, index) {
            var item = snapshot.data!.itemList![index];
            return Videoplayer(
              item: item,
              width: MediaQuery.of(context).size.width,
              heigth: MediaQuery.of(context).size.height,
            );
          });
    } catch (e) {
      return Container(
        width: MediaQuery.of(context).size.width,
        height: MediaQuery.of(context).size.height,
        color: Colors.black,
        child: Center(
            child: Text(
          'Error, Please restart your app again.',
          style: TextStyle(color: Colors.white),
        )),
      );
    }
  } 

其他情况则返回加载状态,因为没有数据返回

另外加载videoplay的时候把width、heigth传入到下一个控件,这样好计算界面呈现的宽度与高度

return Videoplayer(
        item: item,
        width: MediaQuery.of(context).size.width,
        heigth: MediaQuery.of(context).size.height,
      );

请继续关注本博客,其他页面持续更新完成,源码地址:telsavideo, 欢迎fork和star,谢谢!!!

再次奉上演示地址:
前端地址:https://www.pgyer.com/dtok
后端服务器地址:http://47.95.209.198:8181/


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK