Flutter Tik Tok-like video player

Tik Tok video player has many elements in a screen. We use Stack to correctly position them where we want them to be. Following is the snippet of how the screen layout should be:

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));
  }
}
Scroll to Top

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

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close