Flutter UI: Instagram Desktop
By: Yudi Setiawan
Tags: Flutter Desktop, UI Design, Flutter, Flutter Widget, Flutter Ui

Saya rasa hasilnya benar-benar cukup memuaskan ya. 😃

Mulai Pembuatan Projek

Sebelum memulai pembuatan projeknya saya ingin memberitahukan diawal bahwa pada tulisan ini yang saya bahas adalah sebatas menduplikasi design UI-nya. Jadi, UI-nya itu belum ada interaksi ataupun event yang bisa diklik. Dan untuk masalah responsive pun tidak akan saya bahas pada tulisan ini. Oleh karena itu, mohon dimaklumi jika ternyata ketika teman-teman menjalankan aplikasinya dan hasilnya tidak sesuai dengan digambar pada tulisan ini mengingat mungkin resolusi

Requirements

Adapun beberapa persiapan yang perlu kita persiapkan adalah sebagai berikut:

Flutter versi 1.24.0–7–0.pre atau lebih terbaru. Laptop atau monitor dengan minimal resolusi 1440x900. Pastikan bahwa kamu telah meng-enable-kan flutter desktop di lokalmu sesuai dengan OS-nya masing-masing.

# Untuk Windows

flutter config --enable-windows-desktop # Untuk Linux

flutter config --enable-linux-desktop # Untuk MacOS

flutter config --enable-macos-desktop

Buat Projek

Silakan buat projek di IDE favoritmu dan buat nama projeknya instagram_desktop.

Setup pubspec.yaml

Selanjutnya kita perlu setup file pubspec.yaml-nya menjadi seperti berikut.

name: instagram_desktop

description: A new Flutter project.

publish_to: 'none' # Remove this line if you wish to publish to pub.dev

version: 1.0.0+1



environment:

sdk: ">=2.7.0 <3.0.0"



dependencies:

flutter:

sdk: flutter





# The following adds the Cupertino Icons font to your application.

# Use with the CupertinoIcons class for iOS style icons.

cupertino_icons: ^1.0.0



font_awesome_flutter: ^8.10.1

flutter_staggered_grid_view: ^0.3.2

intl: ^0.16.1



dev_dependencies:

flutter_test:

sdk: 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



assets:

- assets/images/



fonts:

- family: Avenir

fonts:

- asset: assets/fonts/TTNorms-Regular.otf

- asset: assets/fonts/TTNorms-Medium.otf

weight: 700

- family: Billabong

fonts:

- asset: assets/fonts/Billabong.ttf

Untuk aset gambar dan font-nya silakan kamu unduh di sini. Lalu, kamu ekstrak file tersebut dan masukkan ke dalam projek seperti gambar berikut.

Persiapkan file aset gambar dan font

Selanjutnya, jangan lupa jalankan perintah berikut.

flutter pub get

Buat Hello World

Jadi, pada tulisan ini kita hanya menggunakan 1 file saja yaitu file main.dart. Untuk pertama-tama silakan ubah kode di dalam file main.dart menjadi seperti berikut.

Dan berikut adalah outputnya.

Output 1

Buat Layout Dasar

Sekarang kita akan buat layout dasarnya. Silakan buka file main.dart dan ubah kode didalamnya menjadi seperti berikut.

Buat Widget Navigation Drawer

Selanjutnya kita ubah kode di dalam WidgetNavigationDrawer menjadi seperti berikut.

Buat Fungsi _buildWidgetInstagramLogo

Langkah berikutnya kita akan membuat widget yang menampilkan logo Instagram. Untuk membuatnya kita perlu buat satu fungsi yang bernama _buildWidgetInstagramLogo dan isi dengan kode berikut.

Widget _buildWidgetInstagramLogo() {

return Padding(

padding: const EdgeInsets.only(

left: 24,

top: 24,

),

child: Row(

children: [

Image.asset(

'assets/images/instagram_logo.png',

width: 32,

),

SizedBox(width: 16),

Text(

'Instagram',

style: TextStyle(

fontFamily: fontFamilyBillabong,

color: Colors.white,

fontSize: 28,

),

),

],

),

);

}

Selanjutnya, kita ubah kode todo berikut.

// TODO: buat fungsi widget bernama '_buildWidgetInstagramLogo'

Menjadi seperti berikut.

_buildWidgetInstagramLogo(),

SizedBox(height: 24),

Berikut adalah perubahan lengkapnya.

Dan outputnya akan menjadi seperti berikut.

Output 2

Buat Fungsi _buildWidgetPhotoProfile

Langkah selanjutnya adalah kita akan membuat fungsi yang menampilkan widget foto profil si pengguna. Silakan buat fungsi dengan nama _buildWidgetPhotoProfile dan isi dengan kode berikut.

Widget _buildWidgetPhotoProfile() {

return Center(

child: Container(

decoration: BoxDecoration(

shape: BoxShape.circle,

gradient: LinearGradient(

begin: Alignment.bottomLeft,

end: Alignment.topRight,

colors: [

color3,

color4,

],

stops: [

0.1,

0.9,

],

),

),

padding: EdgeInsets.all(2),

child: Container(

decoration: BoxDecoration(

shape: BoxShape.circle,

color: color1,

),

padding: EdgeInsets.all(4),

child: ClipOval(

child: Image.asset(

'assets/images/photo_profile.jpg',

width: 72,

height: 72,

fit: BoxFit.cover,

),

),

),

),

);

}

Dan ubah kode todo berikut.

// TODO: buat fungsi widget bernama '_buildWidgetPhotoProfile'

Menjadi seperti berikut.

_buildWidgetPhotoProfile(),

SizedBox(height: 16),

Outputnya akan menjadi seperti berikut.

Output 3

Buat Widget Text Hello Ditta

Kemudian kita akan membuat satu widget yang akan menampilkan teks ‘Hello Ditta’. Untuk membuatnya, silakan ubah kode todo berikut.

// TODO: buat widget yang menampilkan teks 'Hello Ditta'

Menjadi seperti berikut.

Center(

child: Text(

'Hello Ditta',

style: Theme.of(context).textTheme.headline6.copyWith(

color: Colors.white,

fontFamily: fontFamilyAvenir,

),

),

),

Output 4

Buat Widget Text helloditta

Kemudian, kita akan menampilkan teks username-nya si pengguna. Pada kali ini kita contohkan nama username-nya helloditta. Untuk membuatnya silakan kita ubah kode todo berikut.

// TODO: buat widget yang menampilkan teks '@helloditta'

Menjadi seperti berikut.

Center(

child: Text(

'@helloditta',

style: Theme.of(context).textTheme.bodyText2.copyWith(

fontFamily: fontFamilyAvenir,

color: Colors.grey,

),

),

),

Output 5

Buat Fungsi _buildWidgetInfoPostsFollowersFollowing

Selanjutnya kita akan menampilkan info jumlah post, followers, dan following si pengguna. Silakan buat fungsi dengan nama _buildWidgetInfoPostsFollowersFollowing lalu, isi dengan kode berikut.

Widget _buildWidgetInfoPostsFollowersFollowing(BuildContext context) {

return Row(

mainAxisAlignment: MainAxisAlignment.center,

children: [

Expanded(

child: Column(

children: [

Text(

'702',

style: Theme.of(context).textTheme.bodyText2.copyWith(

color: Colors.white,

fontFamily: fontFamilyAvenir,

),

),

SizedBox(height: 8),

Text(

'Posts',

style: Theme.of(context).textTheme.caption.copyWith(

color: Colors.grey,

),

),

],

),

),

Container(

width: 0.5,

height: 48,

color: Colors.grey,

),

Expanded(

child: Column(

children: [

Text(

'88.8k',

style: Theme.of(context).textTheme.bodyText2.copyWith(

color: Colors.white,

fontFamily: fontFamilyAvenir,

),

),

SizedBox(height: 8),

Text(

'Followers',

style: Theme.of(context).textTheme.caption.copyWith(

color: Colors.grey,

),

),

],

),

),

Container(

width: 0.5,

height: 48,

color: Colors.grey,

),

Expanded(

child: Column(

children: [

Text(

'995',

style: Theme.of(context).textTheme.bodyText2.copyWith(

color: Colors.white,

fontFamily: fontFamilyAvenir,

),

),

SizedBox(height: 8),

Text(

'Following',

style: Theme.of(context).textTheme.caption.copyWith(

color: Colors.grey,

),

),

],

),

),

],

);

}

Selanjutnya, kita ubah kode todo berikut.

// TODO: buat fungsi widget bernama '_buildWidgetInfoPostsFollowersFollowing'

Menjadi seperti berikut.

SizedBox(height: 16),

_buildWidgetInfoPostsFollowersFollowing(context),

Output 6

Buat Widget List Menu

Langkah berikutnya adalah kita akan menampilkan list menu Feed, Explore, Notifications, Direct, IG TV, Stats, dan Settings. Untuk membuatnya kita perlu buat fungsi dengan nama _buildWidgetItemMenu dan isikan dengan kode berikut.

Widget _buildWidgetItemMenu(

BuildContext context,

String label,

IconData iconData, {

bool isSelected = false,

int badge = 0,

}) {

return Padding(

padding: const EdgeInsets.only(

left: 24.0,

top: 12.0,

bottom: 12,

),

child: Row(

children: [

isSelected

? ShaderMask(

shaderCallback: (bounds) => LinearGradient(

begin: Alignment.bottomLeft,

end: Alignment.topRight,

colors: [

color3,

color4,

],

stops: [

0.1,

0.9,

],

).createShader(bounds),

child: Icon(

iconData,

color: Colors.white,

),

)

: Icon(

iconData,

color: Colors.white60,

),

SizedBox(width: 24),

Expanded(

child: Text(

label,

style: TextStyle(

color: isSelected ? Colors.white : Colors.white60,

),

),

),

badge > 0

? Expanded(

child: Text(

'$badge',

style: Theme.of(context).textTheme.caption.copyWith(color: Colors.grey.withOpacity(.5)),

textAlign: TextAlign.start,

),

)

: Container(),

SizedBox(width: 16),

isSelected

? Container(

width: 2,

height: 24,

decoration: BoxDecoration(

borderRadius: BorderRadius.circular(999),

color: color3,

),

)

: Container(),

],

),

);

}

Lalu, kita ubah kode todo berikut.

// TODO: buat fungsi widget bernama '_buildWidgetItemMenu' dan menampilkan setiap menu item

Menjadi seperti berikut.

SizedBox(height: 24),

_buildWidgetItemMenu(

context,

'Feed',

Icons.dashboard_outlined,

isSelected: true,

),

_buildWidgetItemMenu(

context,

'Explore',

Icons.search,

),

_buildWidgetItemMenu(

context,

'Notifications',

Icons.notifications,

badge: 4,

),

_buildWidgetItemMenu(

context,

'Direct',

Icons.near_me,

badge: 1,

),

_buildWidgetItemMenu(

context,

'IG TV',

Icons.tv,

),

_buildWidgetItemMenu(

context,

'Stats',

Icons.bar_chart,

),

_buildWidgetItemMenu(

context,

'Settings',

Icons.settings,

),

SizedBox(height: 16),

Sampai langkah ini akan saya tunjukkan kode lengkapnya sekarang seperti berikut.

Dan berikut ialah outputnya.

Output 7

Buat Widget Garis Horizontal

Langkah berikutnya ialah kita akan menampilkan garis horizontal. Untuk membuatnya silakan ubah kode todo berikut.

// TODO: buat widget yang menampilkan garis horizontal

Menjadi seperti berikut.

Padding(

padding: const EdgeInsets.symmetric(horizontal: 24.0),

child: Container(

width: double.infinity,

height: 0.5,

color: Colors.grey,

),

),

SizedBox(height: 16),

Output 8

Buat Menu Logout

Dan widget terakhir yang akan kita buat didalam WidgetNavigationDrawer adalah kita akan menampilkan menu Logout. Untuk membuatnya silakan ubah kode todo berikut.

// TODO: buat fungsi widget bernama '_buildWidgetItemMenu' yang menampilkan menu logout

Menjadi seperti berikut.

_buildWidgetItemMenu(

context,

'Logout',

Icons.logout,

),

Output 9

Sampai di sini akan saya tunjukkan kode lengkapnya agar teman-teman bisa mengikutinya dengan baik.

Buat Widget Content

Sekarang kita akan menuju ke dalam WidgetContent . Silakan kita ubah kode didalam widget tersebut menjadi seperti berikut.

Buat Fungsi _buildWidgetTextFieldSearch

Sekarang kita akan menampilkan widget TextField pencarian. Cara membuatnya adalah kita buat satu fungsi dengan nama _buildWidgetTextFieldSearch dan isikan dengan kode berikut.

Widget _buildWidgetTextFieldSearch(double widthScreen) {

return Expanded(

child: Container(

decoration: BoxDecoration(

color: Color(0xFF484B5B),

borderRadius: BorderRadius.circular(8),

),

padding: const EdgeInsets.only(

left: 16,

right: 16,

),

child: TextField(

decoration: InputDecoration(

hintText: 'Search',

hintStyle: TextStyle(

fontFamily: fontFamilyAvenir,

color: Colors.grey,

),

border: InputBorder.none,

disabledBorder: InputBorder.none,

enabledBorder: InputBorder.none,

errorBorder: InputBorder.none,

focusedBorder: InputBorder.none,

focusedErrorBorder: InputBorder.none,

icon: Icon(

Icons.search,

color: Colors.white60,

),

),

style: TextStyle(

color: Colors.white60,

fontFamily: fontFamilyAvenir,

),

),

),

);

}

Lalu, kita ubah kode todo berikut.

// TODO: buat fungsi widget bernama '_buildWidgetTextFieldSearch'

Menjadi seperti berikut.

_buildWidgetTextFieldSearch(widthScreen),

Output 10

Buat Fungsi _buildWidgetIconHeaderNotification

Langkah selanjutnya adalah kita akan membuat fungsi widget yang akan menampilkan icon notification dibagian header. Untuk membuatnya silakan buat fungsi baru dengan nama _buildWidgetIconHeaderNotification dan isi dengan kode berikut.

Widget _buildWidgetIconHeaderNotification() {

return SizedBox(

width: 32,

height: 32,

child: Stack(

children: [

Align(

alignment: Alignment.center,

child: Icon(

Icons.notifications_outlined,

color: Colors.white60,

),

),

Positioned(

right: 0,

child: Container(

width: 4,

height: 4,

decoration: BoxDecoration(

color: color3,

shape: BoxShape.circle,

),

),

),

],

),

);

}

Lalu, ubah kode todo berikut.

// TODO: buat fungsi widget bernama '_buildWidgetIconHeaderNotification'

Menjadi seperti berikut.

_buildWidgetIconHeaderNotification(),

SizedBox(width: 32),

Output 11

Buat Fungsi _buildWidgetIconHeaderDirect

Langkah selanjutnya adalah kita akan menampilkan icon direct dibagian header-nya. Caranya adalah kita buat fungsi baru dengan nama _buildWidgetIconHeaderDirect dan isi dengan kode berikut.

Widget _buildWidgetIconHeaderDirect() {

return SizedBox(

width: 32,

height: 32,

child: Stack(

children: [

Align(

alignment: Alignment.center,

child: Icon(

Icons.near_me,

color: Colors.white60,

),

),

Positioned(

right: 0,

child: Container(

width: 4,

height: 4,

decoration: BoxDecoration(

color: color3,

shape: BoxShape.circle,

),

),

),

],

),

);

}

Lalu, kita ubah kode todo berikut.

// TODO: buat fungsi widget bernama '_buildWidgetIconHeaderDirect'

Menjadi seperti berikut.

_buildWidgetIconHeaderDirect(),

SizedBox(width: 32),

Output 12

Buat Fungsi _buildWidgetButtonAddPhoto

Sekarang kita akan menampilkan satu button dengan teks Add Photo didalamnya. Untuk membuatnya silakan buat fungsi baru dengan nama _buildWidgetButtonAddPhoto dan isi dengan kode berikut.

Widget _buildWidgetButtonAddPhoto() {

return GestureDetector(

onTap: () {

},

child: Container(

padding: const EdgeInsets.all(16),

decoration: BoxDecoration(

borderRadius: BorderRadius.circular(8),

gradient: LinearGradient(

begin: Alignment.bottomLeft,

end: Alignment.topRight,

colors: [

color3,

color4,

],

stops: [

0.4,

0.9,

],

),

),

child: Row(

children: [

Container(

decoration: BoxDecoration(

shape: BoxShape.circle,

color: Colors.white30,

),

padding: EdgeInsets.all(2),

child: Icon(

Icons.add,

color: Colors.white,

size: 16,

),

),

SizedBox(width: 8),

Text(

'Add photo',

style: TextStyle(

color: Colors.white,

fontFamily: fontFamilyAvenir,

),

),

],

),

),

);

}

Lalu, kita ubah kode todo berikut.

// TODO: buat fungsi widget bernama '_buildWidgetButtonAddPhoto'

Menjadi seperti berikut.

_buildWidgetButtonAddPhoto(),

Output 13

Buat fungsi _buildWidgetTitleStories

Selanjutnya adalah kita akan menampilkan teks Stories beserta dengan tombol watch all. Untuk membuatnya kita perlu membuat fungsi baru dengan nama _buildWidgetTitleStories dan isi dengan kode berikut.

Widget _buildWidgetTitleStories(BuildContext context) {

return Row(

crossAxisAlignment: CrossAxisAlignment.end,

children: [

Text(

'Stories',

style: Theme.of(context).textTheme.headline5.copyWith(

color: Colors.white,

fontFamily: fontFamilyAvenir,

fontWeight: FontWeight.bold,

),

),

Expanded(

child: Row(

mainAxisAlignment: MainAxisAlignment.end,

children: [

Container(

decoration: BoxDecoration(

shape: BoxShape.circle,

border: Border.all(color: Colors.white60),

),

padding: const EdgeInsets.all(4),

child: Icon(

Icons.play_arrow,

color: Colors.white,

size: 14,

),

),

SizedBox(width: 8),

Text(

'Watch all',

style: TextStyle(

color: Colors.white60,

fontFamily: fontFamilyAvenir,

),

),

],

),

),

],

);

}

Lalu kita ubah kode todo berikut.

// TODO: buat fungsi widget bernama '_buildWidgetTitleStories

Menjadi seperti berikut.

SizedBox(height: 32),

_buildWidgetTitleStories(context),

Output 14

Buat Class WidgetStories

Langkah berikutnya adalah kita akan menampilkan list story pengguna lain secara horizontal. Untuk membuatnya kita perlu membuat class baru dengan nama WidgetStories dan isi dengan kode berikut.

Lalu, kita ubah kembali kode todo berikut.

// TODO: buat class WidgetStories

Menjadi seperti berikut.

SizedBox(height: 24),

WidgetStories(),

Output 15

Buat Fungsi _buildWidgetTitleFeedLatestPopular

Langkah berikutnya adalah kita akan membuat fungsi yang bernama _buildWidgetTitleFeedLatestPopular dimana, output dari fungsi tersebut adalah akan menampilkan teks Feed dan filter Latest dengan Popular.

Widget _buildWidgetTitleFeedLatestPopular(BuildContext context) {

return Row(

children: [

Text(

'Feed',

style: Theme.of(context).textTheme.headline5.copyWith(

color: Colors.white,

fontFamily: fontFamilyAvenir,

fontWeight: FontWeight.bold,

),

),

Expanded(

child: Row(

mainAxisAlignment: MainAxisAlignment.end,

crossAxisAlignment: CrossAxisAlignment.end,

children: [

Column(

children: [

Text(

'Latest',

style: TextStyle(

color: Colors.white,

),

),

SizedBox(height: 8),

Container(

width: 4,

height: 4,

decoration: BoxDecoration(

color: Colors.white,

borderRadius: BorderRadius.circular(999),

),

),

],

),

SizedBox(width: 32),

Column(

children: [

Text(

'Popular',

style: TextStyle(

color: Colors.grey,

),

),

SizedBox(height: 8),

Container(

width: 4,

height: 4,

),

],

),

],

),

),

],

);

}

Selanjutnya kita ubah kode todo berikut.

// TODO: buat fungsi widget bernama '_buildWidgetTitleFeedLatestPopular'

Menjadi seperti berikut.

SizedBox(height: 32),

_buildWidgetTitleFeedLatestPopular(context),

Output 16

Buat WidgetFeed

Langkah terakhir adalah kita akan menampilkan list posts dari pengguna lain menggunakan StaggeredView. Untuk membuatnya silakan buat class baru dengan nama WidgetFeed dan isikan dengan kode berikut.

Jika ada error jangan lupa diimport class-nya ya. Berikut adalah kode yang perlu kita import.

import 'package:flutter/cupertino.dart';

import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';

import 'package:intl/intl.dart';

Lalu, kita ubah kode todo berikut.

// TODO: buat class WidgetFeed

Menjadi seperti berikut.

SizedBox(height: 24),

WidgetFeed(),

Dan berikut adalah output terakhirnya.

Output 17

Kesimpulan

Akhirnya selesai juga ya. Jadi, setelah saya lihat hasilnya ternyata Flutter Desktop mampu memberikan kesan pertama yang luar biasa bagi saya. Saya ingat benar bahwa dulu di Java Desktop untuk membuat tampilan seperti UI diatas benar-benar susah loh. Namun, sekarang semakin berkembangnya bahasa pemrograman dan framework maka, untuk membuat UI yang keren pun juga semakin lebih mudah. Untuk source code lengkapnya bisa kamu lihat di Github berikut ya.