- Published on
How to build TikTok with Flutter? Part 1
- Authors
- Name
- Loi Tran
- @PrimeTimeTrann
How to build TikTok with Flutter?
In this tutorial series we'll clone TikTok using Flutter.
Along the way we'll learn how to use the core widgets of Flutter.
We'll begin by implementing the navigation features of TikTok.
Specifically, we'll create a BottomNavigationBar. This navigation bar will allow the user to access different content on different screens.
Create New Project
Instantiate a new project and run it.
flutter create fluttok
cd fluttok
flutter run
We're using Flutter 3.0.2
, Dart 2.17.3
• DevTools 2.12.2
Replace everything inside of main.dart
import 'package:flutter/material.dart';
import 'package:fluttok/navigation/DrawerNav.dart';
main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const MaterialApp(
home: DrawerNav(),
debugShowCheckedModeBanner: false,
);
}
}
We remove all the comments and do the following.
- Line 2 Import a custom widget,
DrawerNav
. - Line 14 Pass the
DrawerNav
widget as a parameter toMaterialApp
, specifically thehome
parameter. - Line 15 Hide the debug banner by passing
false
to thedebugShowCheckedModeBanner
parameter ofMaterialApp
.
Create Drawer Navigator
Structure/place project folders and files following best practice.
.
├── ...
├── pubspec.yaml
└── lib/
├── navigation/ # Create this folder
│ └── DrawerNav.dart # Create this file
└── main.dart
The path matches the import statement we used a moment ago, where lib
is the name of our project, fluttok
.
import 'package:fluttok/navigation/DrawerNav.dart';
Define DrawerNav
as a stateful widget inside of DrawerNav.dart
. We need it to be stateful so we can navigate between the multiple pages/screens using the BottomNavigationBar
// ./lib/navigation/DrawerNav.dart
import 'package:flutter/material.dart';
class TikTokPage extends StatefulWidget {
final MaterialColor color;
const TikTokPage({Key? key, required this.color}) : super(key: key);
State<TikTokPage> createState() => _TikTokPageState();
}
class _TikTokPageState extends State<TikTokPage> {
Widget build(BuildContext context) {
return Container(color: widget.color);
}
}
class DrawerNav extends StatefulWidget {
const DrawerNav({Key? key}) : super(key: key);
State<DrawerNav> createState() => _DrawerNav();
}
class _DrawerNav extends State<DrawerNav> {
int _selectedIndex = 0;
static List<Widget> get _widgetOptions => <Widget>[
const TikTokPage(color: Colors.yellow),
const TikTokPage(color: Colors.blue),
const TikTokPage(color: Colors.green),
const TikTokPage(color: Colors.teal),
const TikTokPage(color: Colors.pink),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
Widget build(BuildContext context) {
return Scaffold(
body: _widgetOptions.elementAt(_selectedIndex),
bottomNavigationBar: BottomNavigationBar(
elevation: 0,
onTap: _onItemTapped,
showUnselectedLabels: true,
currentIndex: _selectedIndex,
type: BottomNavigationBarType.fixed,
selectedItemColor: Colors.black87,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
label: 'Home',
icon: Icon(Icons.home),
),
BottomNavigationBarItem(
label: 'Discover',
icon: Icon(Icons.arrow_circle_up_sharp),
),
BottomNavigationBarItem(
label: '',
icon: Icon(Icons.add)
),
BottomNavigationBarItem(
label: 'Inbox',
icon: Icon(Icons.inbox),
),
BottomNavigationBarItem(
label: 'Profile',
icon: Icon(Icons.account_box_rounded),
),
],
),
);
}
}
Refresh and we'll our app works and has a bottom navigation bar, awesome.
There's a lot going on so let's review.
class TikTokPage extends StatefulWidget {
final MaterialColor color;
const TikTokPage({Key? key, required this.color}) : super(key: key);
State<TikTokPage> createState() => _TikTokPageState();
}
class _TikTokPageState extends State<TikTokPage> {
Widget build(BuildContext context) {
return Container(color: widget.color);
}
}
The TikTokPage
widget is a placeholder. The interesting part of this widget is that it requires a parameter color
when created.
- Line 2: Define
color
property for theTikTokPage
class/widget. - Line 4: Require the
color
parameter in the constructor of theTikTokPage
widget. - Line 2: Consume the
color
parameter in the build method ofTikTokPage
, producing a dynamic background colors for each page/screen.
This pattern/technique is common with other JS frameworks such as React & Vue as well.
Create Bottom Navigator Bar
Key use of the Bottom Navigation Bar and it's required parameters/properties.
class _DrawerNav extends State<DrawerNav> {
int _selectedIndex = 0;
static List<Widget> get _widgetOptions => <Widget>[
const TikTokPage(color: Colors.yellow),
const TikTokPage(color: Colors.blue),
const TikTokPage(color: Colors.green),
const TikTokPage(color: Colors.teal),
const TikTokPage(color: Colors.pink),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
Widget build(BuildContext context) {
return Scaffold(
body: _widgetOptions.elementAt(_selectedIndex),
bottomNavigationBar: BottomNavigationBar(
elevation: 0,
onTap: _onItemTapped,
showUnselectedLabels: true,
currentIndex: _selectedIndex,
type: BottomNavigationBarType.fixed,
selectedItemColor: Colors.black87,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
label: 'Home',
icon: Icon(Icons.home),
),
BottomNavigationBarItem(
label: 'Discover',
icon: Icon(Icons.arrow_circle_up_sharp),
),
BottomNavigationBarItem(
label: '',
icon: Icon(Icons.add)
),
BottomNavigationBarItem(
label: 'Inbox',
icon: Icon(Icons.inbox),
),
BottomNavigationBarItem(
label: 'Profile',
icon: Icon(Icons.account_box_rounded),
),
],
),
);
}
}
- Line 2: Define
_selectedIndex
property which defaults to 0. This is which tab is selected. - Lines 4-10: Define array of page widgets which will be the pages/screens in the bottom navigation bar. Notice how each instance of TikTokPage is passed a different
color
parameter. - Lines 12-16: Define handler for when a tab bar item is tapped by the user.
- Lines 21: Select one page/screen inside of,
_widgetOptions
, to pass to theScaffold
widget'sbody
parameter. We use the_selectedIndex
state variable to identify which element/item/page/screen to pass. - Lines 24: Pass
_onItemTapped
to the parameteronTap
of `Scaffold. Once again this handles changing the page/screen viewed/focused. - Lines 29-50: Define the bottom navigation bar items including their label and icon.
Refactor TikTokPage
Let's extract the TikTokPage
widget to it's own file so we can follow best practices.
.
├── ...
├── pubspec.yaml
└── lib/
├── navigation/
│ └── DrawerNav.dart
├── pages/ # Create this folder
│ └── TikTokPage.dart # Create this file
└── main.dart
Cut and paste the TikTokPage
widget to the TikTokPage.dart
file.
import 'package:flutter/material.dart';
class TikTokPage extends StatefulWidget {
final MaterialColor color;
const TikTokPage({Key? key, required this.color}) : super(key: key);
State<TikTokPage> createState() => _TikTokPageState();
}
class _TikTokPageState extends State<TikTokPage> {
Widget build(BuildContext context) {
return Container(color: widget.color);
}
}
Import the TikTokPage
file into the DrawerNav.dart
file
// ./lib/navigation/DrawerNav.dart
import 'package:flutter/material.dart';
import 'package:fluttok/pages/TikTokPage.dart';
Create Media Content Widget
Above the TikTokPage
widget, create a new MediaContent
widget which will contain the logic for each of our videos.
class MediaContent extends StatefulWidget {
const MediaContent({Key? key}) : super(key: key);
State<MediaContent> createState() => _MediaContentState();
}
class _MediaContentState extends State<MediaContent> {
Widget build(BuildContext context) {
return Container(
color: Colors.red,
margin: const EdgeInsets.all(5),
);
}
}
- Line 12: Give the screen a red background so we can more easily identify it.
- Line 13: Give the screen margin so we can more easily identify it.
Create Vertical Scroll through the list of videos
Refactor TikTokPage
to have a PageView in it's build method. This widget implements vertical scroll with the use of a controller
.
class TikTokPage extends StatefulWidget {
final MaterialColor color;
const TikTokPage({Key? key, required this.color}) : super(key: key);
State<TikTokPage> createState() => _TikTokPageState();
}
class _TikTokPageState extends State<TikTokPage> {
PageController controller = PageController(initialPage: 0);
Widget build(BuildContext context) {
return PageView(
controller: controller,
scrollDirection: Axis.vertical,
children: [
for (var i = 0; i < 5; i++) const MediaContent(),
],
);
}
}
Line 11: Instantiate a
PageController
with aninitialPage
of 0 which we'll use to control thePageView
.Line 16: Pass the
PageController
to thecontroller
parameter ofPageView
.Line 19: Use a loop to create several instances of
MediaContent
for testing.
We should now see that we can scroll vertically, excellent.