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)); } }