Beberapa waktu lalu saya sempat menulis tentang bagaimana cara menerapkan animasi pada aplikasi Flutter yang bisa dibaca di sini. Kali ini, saya ingin berbagi tentang salah satu konsep baru yang dihadirkan Flutter.
Sebulan terakhir saya dibuat tertarik dengan salah satu konsep yang dihadirkan oleh Flutter dimana kita bisa menjalankan proyek Flutter menjadi sebuah web pada browser. Keunggulan Flutter yang dapat kita gunakan untuk membuat aplikasi yang cantik pada mobile, kini kita bisa hadirkan pada sebuah website. Wow! Menarik ya!
Walau konsep ini sudah diumumkan jauh sebelum saya mencobanya, hal ini tidak menghentikan niat saya untuk tetap melakukan eksplorasi dan membagi hasilnya. Penasaran? Mari berkenalan dengan Hummingbird yang akan dibahas lebih dalam pada tulisan saya kali ini.
💻 Mulai Belajar Pemrograman
Belajar pemrograman di Dicoding Academy dan mulai perjalanan Anda sebagai developer profesional.
Daftar SekarangApa itu Hummingbird
Pada akhir tahun 2018 tepatnya pada bulan Desember, Google sebagai pengembang Flutter mengumumkan versi yang dapat dijalankan sebagai sebuah web atau yang disebut sebagai proyek Hummingbird. Sederhananya, Hummingbird memiliki arsitektur seperti gambar di bawah ini:
Bisa kita lihat pada gambar di atas bahwa untuk dapat menjalankan proyek Flutter menjadi sebuah website, dibutuhkan core atau inti dari Flutter itu sendiri untuk melakukan drawing pada lapisan teratas standar browser API. Menggunakan kombinasi dari DOM, Canvas, dan CSS, Flutter dapat menyediakan sebuah sistem yang memiliki kualitas tinggi, portable dan tentunya pengalaman pengguna pada setiap browser modern.
Secara teknis, dibutuhkan sebuah kompiler khusus untuk melakukan kompilasi inti dari Flutter dan framework yang berada di dalam project ke dalam sebuah berkas tunggal yang dapat di-deploy ke web server apapun. Menariknya, kita tidak perlu melakukan konfigurasi untuk kompiler tersebut, karena akan dilakukan secara otomatis. Keren kan?
Okay. Talks are cheap, show me the code. Ingat kata siapa itu? Mari coba kita buat proyek Flutter yang dapat dijalankan pada browser. Eits, tidak hanya dapat dijalankan. Kita bahkan akan membuat proyek Flutter dengan tampilan yang responsive pada perubahan ukuran jendela browser. Menarik!
Proyek Web Flutter
Sebelum membuat proyek, kita perlu mengaktifkan terlebih dahulu fitur web untuk Flutter. Karena untuk saat ini, fitur tersebut masih dalam tahap pengembangan. Artinya kita perlu berpindah dari channel stable ke channel beta. Berikut langkah-langkahnya:
- Buka terminal atau command prompt dan jalankan perintah berikut untuk berpindah dari channel stable ke channel beta:
1flutter channel beta - Jika tidak terdapat eror, lanjutkan dengan perintah berikut untuk upgrade Flutter SDK:
1flutter upgrade - Setelah selesai, kita bisa langsung mengaktifkan fitur dukungan web dengan perintah berikut:
1flutter config --enable-web - Untuk memastikan apakah fitur dukungan web sudah aktif, jalankan perintah flutter devices pada terminal. Kamu akan melihat beberapa perangkat yang terkoneksi untuk digunakan seperti berikut:
12342 connected device:Web Server • web-server • web-javascript • Flutter ToolsChrome • chrome • web-javascript • Google Chrome 81.0.4044.129
Sampai saat ini kita sudah bisa membuat proyek baru seperti biasanya dan menjalankannya langsung pada browser. Cukup mudah ya. Penasaran hasilnya seperti apa? Jalankan perintah berikut pada terminal untuk mencobanya:
1 |
flutter run -d chrome |
Tunggu hingga proses kompilasi selesai dan jendela baru chrome terbuka. Kurang lebih akan seperti ini tampilannya:
Responsive Widget
Dalam mengembangkan sebuah website, salah satu yang perlu diperhitungkan adalah seberapa responsive website tersebut terhadap perubahan yang terjadi khususnya pada perubahan tampilan. Lantas bagaimana kita memenuhi kebutuhan tersebut pada proyek Flutter yang notabene pengembangannya berbeda dengan pengembangan website pada umumnya? Serahkan semuanya pada widget MediaQuery dan LayoutBuilder!
Okey, kita mulai dengan membuat kelas helper yang akan menampung beberapa informasi terkait ukuran lebar browser seperti berikut:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
import 'package:flutter/material.dart'; class ScreenHelper { static bool isSmallScreen(BuildContext context) { return MediaQuery.of(context).size.width <= 600; } static bool isNormalScreen(BuildContext context) { return MediaQuery.of(context).size.width > 600 && MediaQuery.of(context).size.width <= 900; } static bool isMediumScreen(BuildContext context) { return MediaQuery.of(context).size.width > 900 && MediaQuery.of(context).size.width <= 1300; } static bool isLargeScreen(BuildContext context) { return MediaQuery.of(context).size.width > 1300; } static String screenType(BuildContext context) { if (isSmallScreen(context)) return "Small Screen"; if (isNormalScreen(context)) return "Normal Screen"; if (isMediumScreen(context)) return "Medium Screen"; if (isLargeScreen(context)) return "Large Screen"; return "undefined"; } } |
Jika kita perhatikan, setiap fungsi yang pada kode di atas terdapat penggunaan MediaQuery untuk mendapatkan informasi ukuran lebar browser dan disesuaikan dengan ketentuan ukuran responsive.
Lanjut, kita akan membuat custom widget GridView dimana gridCount-nya akan menyesuaikan masing-masing ukuran responsive. Dimulai dengan membuat berkas Dart baru dan lengkapi kodenya seperti di bawah ini:
1 2 3 4 5 6 7 8 9 10 |
class GridViewResponsive extends StatefulWidget { @override _GridViewResponsiveState createState() => _GridViewResponsiveState(); } class _GridViewResponsiveState extends State<GridViewResponsive> { @override Widget build(BuildContext context) { return Container(); } |
Fokus pada kelas GridViewResponsive, tambahkan beberapa properti ukuran dan daftar widget yang akan disematkan pada properti children dari widget GridView seperti berikut:
1 2 3 4 5 6 7 8 9 10 11 12 |
class GridViewResponsive extends StatefulWidget { final List<Widget> children; final int smallScreenGridCount; final int normalScreenGridCount; final int mediumScreenGridCount; final int largerScreenGridCount; @override _GridViewResponsiveState createState() => _GridViewResponsiveState(); } |
Kita akan akan mendapati eror di beberapa properti yang sudah ditambahkan. Sans, kita bisa mengatasinya dengan menambahkan constructor untuk menetapkan nilai dari beberapa properti tersebut seperti berikut:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class GridViewResponsive extends StatefulWidget { final List<Widget> children; final int smallScreenGridCount; final int normalScreenGridCount; final int mediumScreenGridCount; final int largerScreenGridCount; const GridViewResponsive( {Key key, this.smallScreenGridCount, this.normalScreenGridCount, this.mediumScreenGridCount, this.largerScreenGridCount, @required this.children}) : super(key: key); @override _GridViewResponsiveState createState() => _GridViewResponsiveState(); } |
Selesai! Tetap semangat ya, lanjut. Masih pada berkas Dart yang sama, kita akan fokus pada kelas _GridViewResponsiveState. Di sini kita akan menyesuaikan sedikit kode didalamnya dengan menggunakan widget LayoutBuilder dan GridView tentunya. Langsung saja ubah isi dari fungsi build() menjadi seperti berikut:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class _GridViewResponsiveState extends State<GridViewResponsive> { @override Widget build(BuildContext context) { return LayoutBuilder(builder: (context, constraints) { int gridCount = 2; return GridView.count( crossAxisCount: gridCount, children: widget.children, ); }); } } |
Jika kita perhatikan, sepertinya masih ada yang kurang dari kode di atas. Yap, gridCount masih bernilai statis yang artinya tidak dapat menyesuaikan perubahan yang berakibat tidak responsive-nya halaman. Lantas bagaimana menentukan nilainya agar bisa dinamis? Mudah, kita bisa menggunakan cara tradisional yaitu dengan menentukannya menggunakan if statement seperti berikut:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class _GridViewResponsiveState extends State<GridViewResponsive> { @override Widget build(BuildContext context) { return LayoutBuilder(builder: (context, constraints) { int gridCount = 2; if (constraints.maxWidth <= 600) { gridCount = widget.smallScreenGridCount ?? 2; } else if (constraints.maxWidth > 600 && constraints.maxWidth <= 900) { gridCount = widget.normalScreenGridCount ?? 4; } else if (constraints.maxWidth > 900 && constraints.maxWidth <= 1200) { gridCount = widget.mediumScreenGridCount ?? 6; } else { gridCount = widget.largerScreenGridCount ?? 8; } return GridView.count( crossAxisCount: gridCount, children: widget.children, ); }); } } |
Nah, kurang lebih berkas Dart yang sudah kita sesuaikan kode di dalamnya akan seperti di bawah ini:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
import 'package:flutter/material.dart'; class GridViewResponsive extends StatefulWidget { final List<Widget> children; final int smallScreenGridCount; final int normalScreenGridCount; final int mediumScreenGridCount; final int largerScreenGridCount; const GridViewResponsive( {Key key, this.smallScreenGridCount, this.normalScreenGridCount, this.mediumScreenGridCount, this.largerScreenGridCount, @required this.children}) : super(key: key); @override _GridViewResponsiveState createState() => _GridViewResponsiveState(); } class _GridViewResponsiveState extends State<GridViewResponsive> { @override Widget build(BuildContext context) { return LayoutBuilder(builder: (context, constraints) { int gridCount = 2; if (constraints.maxWidth <= 600) { gridCount = widget.smallScreenGridCount ?? 2; } else if (constraints.maxWidth > 600 && constraints.maxWidth <= 900) { gridCount = widget.normalScreenGridCount ?? 4; } else if (constraints.maxWidth > 900 && constraints.maxWidth <= 1200) { gridCount = widget.mediumScreenGridCount ?? 6; } else { gridCount = widget.largerScreenGridCount ?? 8; } return GridView.count( crossAxisCount: gridCount, children: widget.children, ); }); } } |
Menggunakan Responsive Widget
Setelah selesai dengan custom widget, kita bisa langsung menggunakan widget tersebut pada main screen. Tapi sebelum itu, kita perlu menyesuaikan terlebih dahulu kelas main.dart menjadi seperti di bawah ini:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( // is not restarted. primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: MyHomePage(title: 'Flutter Grid Responsive'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('${widget.title} [${ScreenHelper.screenType(context)}]'), ), body: Container(), // This trailing comma makes auto-formatting nicer for build methods. ); } } |
Selanjutnya kita akan menambahkan data dan daftar widget yang akan ditampilkan pada GridView. Fokus pada kelas _MyHomePageState kemudian tambahkan beberapa baris kode berikut:
1 2 3 4 5 6 7 8 9 |
class _MyHomePageState extends State<MyHomePage> { List<String> raw = "d, i, c, o, d, i, n, g".toUpperCase().split(","); List<Widget> gridWidget = []; @override Widget build(BuildContext context) { return Scaffold( … ); } } |
Setelah itu, override fungsi initState() untuk melakukan generate daftar widget berdasarkan data yang sudah kita tambahkan seperti di bawah ini:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
class _MyHomePageState extends State<MyHomePage> { List<String> raw = "d, i, c, o, d, i, n, g".toUpperCase().split(","); List<Widget> gridWidget = []; @override void initState() { super.initState(); List.generate( 50, (index) => gridWidget.add( Container( margin: EdgeInsets.all(8), color: Colors.blue, child: Center( child: Text( (raw.toList()..shuffle()).first, style: TextStyle(color: Colors.white, fontSize: 30), ), ), ), ), ); } @override Widget build(BuildContext context) { return Scaffold( ... ); } } |
Terakhir, ubah kode yang berada di dalam fungsi build() dengan menggunakan custom widget yang sudah kita buat seperti berikut:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
class _MyHomePageState extends State<MyHomePage> { List<String> raw = "d, i, c, o, d, i, n, g".toUpperCase().split(","); List<Widget> gridWidget = []; @override void initState() { super.initState(); List.generate( 50, (index) => gridWidget.add( Container( margin: EdgeInsets.all(8), color: Colors.blue, child: Center( child: Text( (raw.toList()..shuffle()).first, style: TextStyle(color: Colors.white, fontSize: 30), ), ), ), ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('${widget.title} [${ScreenHelper.screenType(context)}]'), ), body: Padding( padding: const EdgeInsets.all(8.0), child: GridViewResponsive( children: gridWidget, ), ), // This trailing comma makes auto-formatting nicer for build methods. ); } } |
Masih semangat? Mari kita coba jalankan proyek web flutter kita. Jika tidak terdapat eror saat kompilasi, tampilannya akan seperti di bawah ini:
Bagaimana? Sangat menarik bukan? Tidak sampai disitu loh, kita bisa menentukkan sendiri gridCount sesuai dengan ukuran responsive. Contohnya seperti berikut:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('${widget.title} [${ScreenHelper.screenType(context)}]'), ), body: Padding( padding: const EdgeInsets.all(8.0), child: GridViewResponsive( smallScreenGridCount: 2, normalScreenGridCount: 4, mediumScreenGridCount: 8, largerScreenGridCount: 18, children: gridWidget, ), ), // This trailing comma makes auto-formatting nicer for build methods. ); } |
Dengan segala kemudahannya, apakah Hummingbird sudah dapat digunakan untuk proyek production? Hm, jika dilihat statusnya yang masih berada pada channel beta, sepertinya saat ini belum disarankan untuk kita gunakan. Untuk itu, mari kita tunggu saja update-update terbaru tentang hummingbird.
Bonus
Sebagai penutup, saya mempunyai project web Flutter hasil redesign halaman utama dicoding dengan tampilan seperti berikut:
Proyek di atas dapat dilihat pada tautan github saya di sini. Sebenarnya proyek di atas masih belum selesai. Jadi, saya ingin menantang teman-teman untuk PR dengan menyelesaikan bagian yang belum selesai tersebut. Challenge Accepted?
Okey, mungkin segitu dulu tulisan saya kali ini. Ada yang kurang jelas atau ingin ditanyakan? Yuk tulis komentar di kolom yang sudah tersedia di bawah ya.