Skip to main content

How to Create a Tik Tok-like Video Player in Flutter?

Tik Tok has taken the world by storm, with its short, creative videos. TikTok is a social media app where users can create and share short videos with each other. The app is divided into two sections: For You and Following. For You shows videos that have been uploaded by the user’s friends and Following shows videos that have been uploaded by other users that the user is following. Users can add music to their videos, which are then played in a loop. They can also add filters and effects to their videos.

What if you want to create a Tik Tok-like video player in Flutter? In this blog post, we’ll show you the basic layout snippet.

import 'dart:math';
import 'dart:ui';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: VideoScreen(),
        bottomNavigationBar: BottomNavigationBar(
          type: BottomNavigationBarType.fixed,
          unselectedItemColor: Colors.white,
          backgroundColor: Colors.black,
          currentIndex: 0, // this will be set when a new tab is tapped
          items: [
            BottomNavigationBarItem(
              icon: TtIcon(iconData: Icons.home, size: 36),
              label: 'Home',
            ),
            BottomNavigationBarItem(
              icon: TtIcon(iconData: Icons.search, size: 36),
              label: 'Discover',
            ),
            BottomNavigationBarItem(
              icon: TtIcon(iconData: Icons.add, size: 36),
              label: ''
            ),
            BottomNavigationBarItem(
              icon: TtIcon(iconData: Icons.message, size: 36),
              label: 'Inbox',
            ),
            BottomNavigationBarItem(
              icon: TtIcon(iconData: Icons.account_circle, size: 36),
              label: 'Me',
            )
          ],
        ));
  }
}

class VideoScreen extends StatefulWidget {
  @override
  _VideoScreenState createState() => _VideoScreenState();
}

class _VideoScreenState extends State<VideoScreen> {
  VideoPlayerController _videoController;
  Future<void> _initializeVideoPlayerFuture;
  final videos = [
    'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
    'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4',
    'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4',
    'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/WeAreGoingOnBullrun.mp4'
  ];

  @override
  void initState() {
    _videoController =
        VideoPlayerController.network(videos[Random().nextInt(4)]);
    _initializeVideoPlayerFuture = _videoController.initialize();
    _videoController.play();
    _videoController.setLooping(true);

    super.initState();
  }

  @override
  void dispose() {
    _videoController.dispose();
    super.dispose();
  }

  Widget _video(BuildContext context) {
    return Container(
      color: Colors.black,
      width: MediaQuery.of(context).size.width,
      height: MediaQuery.of(context).size.height,
      child: FutureBuilder(
        future: _initializeVideoPlayerFuture,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            return AspectRatio(
              aspectRatio: _videoController.value.aspectRatio,
              child: VideoPlayer(_videoController),
            );
          } else {
            return Center(child: CircularProgressIndicator());
          }
        },
      ),
    );
  }

  Widget _top() {
    return Align(
      alignment: Alignment.topCenter,
      child: Container(
        padding: EdgeInsets.all(16.0),
        width: double.infinity,
        height: 50,
        color: Colors.transparent,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            TtText(text: 'Following', size: 18),
            VerticalDivider(
              color: Colors.white,
            ),
            TtText(
              text: 'For You',
              size: 18,
            ),
          ],
        ),
      ),
    );
  }

  Widget _right(BuildContext context) {
    return Align(
      alignment: Alignment.bottomRight,
      child: Container(
        width: 60,
        height: MediaQuery.of(context).size.height / 2,
        color: Colors.transparent,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Container(
              height: 60,
              child: Stack(
                children: [
                  CircleAvatar(
                      radius: 24,
                      backgroundImage: NetworkImage(
                          'https://images.unsplash.com/photo-1611575330633-551252bafad7?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=200&ixlib=rb-1.2.1&q=80&w=300')),
                  Positioned(
                    top: 40,
                    left: 17,
                    child: ClipOval(
                      child: Material(
                        color: Colors.red, 
                        child: InkWell(
                          splashColor: Colors.orange,
                          child: SizedBox(
                              width: 16,
                              height: 16,
                              child: Icon(
                                Icons.add,
                                color: Colors.white,
                                size: 14,
                              )),
                          onTap: () {},
                        ),
                      ),
                    ),
                  )
                ],
              ),
            ),
            SizedBox(height: 16),
            IconButton(
              icon: TtIcon(
                iconData: Icons.favorite,
                size: 36,
              ),
              onPressed: () {},
            ),
            TtText(text: '856', size: 18),
            SizedBox(height: 16),
            IconButton(
              icon: TtIcon(iconData: Icons.comment, size: 36),
              onPressed: () {},
            ),
            TtText(text: '35', size: 18),
            SizedBox(height: 16),
            IconButton(
              icon: TtIcon(iconData: Icons.send, size: 36),
              onPressed: () {},
            ),
            TtText(
              text: '1',
              size: 18,
            ),
          ],
        ),
      ),
    );
  }

  Widget _bottom() {
    return Align(
      alignment: Alignment.bottomLeft,
      child: Container(
        padding: EdgeInsets.all(8),
        width: double.infinity,
        height: 120,
        color: Colors.transparent,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Container(
                  decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(5),
                    color: Colors.grey,
                  ),
                  padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
                  child: Row(children: [
                    TtIcon(
                      iconData: Icons.shopping_cart,
                      size: 16,
                    ),
                    TtText(text: 'Shop', size: 18),
                  ]),
                ),
              ],
            ),
            SizedBox(height: 6),
            Row(
              children: [
                TtText(text: '@account_name', size: 18),
                Icon(
                  Icons.check_circle,
                  color: Colors.blue,
                )
              ],
            ),
            SizedBox(height: 6),
            TtText(
                text: 'This is caption #hashtag #hashtag #hashtag', size: 13),
            SizedBox(height: 6),
            Row(
              children: [
                TtIcon(iconData: Icons.music_note, size: 14),
                TtText(text: 'Original Sound', size: 14),
              ],
            ),
          ],
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      fit: StackFit.expand,
      children: [
        _video(context),
        _top(),
        _bottom(),
        _right(context),
      ],
    );
  }
}

class TtIcon extends StatelessWidget {
  final IconData iconData;
  final double size;

  const TtIcon({Key key, this.iconData, this.size}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Icon(iconData, size: this.size, color: Colors.white);
  }
}

class TtText extends StatelessWidget {
  final String text;
  final double size;

  const TtText({Key key, this.text, this.size}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(text,
        style: TextStyle(color: Colors.white, fontSize: this.size));
  }
}

By continuing to use the site, you agree to the use of cookies.