Latihan Flutter Pertama - Namer App
Nama : Helsa Nesta Dhaifullah
NRP : 5025201005
Kelas : PPB I - 2024
Di kesempatan ini, kita akan belajar framework baru untuk membuat aplikasi
Android, yaitu Flutter. Untuk memulainya, perlu untuk
download Flutter SDK dan Visual Studio Code, sebagai kode editorial
(IDE). Aplikasi yang akan dibuat adalah Namer App. Berikut adalah hasil akhir dari aplikasi yang akan kita buat.
Membuat Proyek Flutter
Membuat proyek Flutter pertama Anda
Luncurkan Visual Studio Code dan buka palet perintah (dengan
F1 atau Ctrl+Shift+P atau Shift+Cmd+P). Ketik "flutter new". Pilih perintah Flutter: New Project.
Berikutnya, pilih Application lalu folder tempat proyek akan dibuat. Folder
ini dapat berupa direktori utama kita, atau direktori seperti
C:\src\. Terakhir, beri nama proyek seperti namer_app atau
my_awesome_namer.
Menyalin & Menempelkan aplikasi awal
Pada panel sebelah kiri VS Code, cari lalu buka file
pubspec.yaml.
name: namer_app description: A new Flutter project. publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 0.0.1+1 environment: sdk: '>=2.19.4 <4.0.0' dependencies: flutter: sdk: flutter english_words: ^4.0.0 provider: ^6.0.0 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^2.0.0 flutter: uses-material-design: true
Berikutnya, buka file konfigurasi lainnya dalam proyek tersebut,
analysis_options.yaml. Ganti konten file tersebut dengan kode berikut:
include: package:flutter_lints/flutter.yaml linter: rules: prefer_const_constructors: false prefer_final_fields: false use_key_in_widget_constructors: false prefer_const_literals_to_create_immutables: false prefer_const_constructors_in_immutables: false avoid_print: false
Terakhir, buka file main.dart pada direktori
lib/. Ganti konten file ini dengan kode berikut:
import 'package:english_words/english_words.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return ChangeNotifierProvider( create: (context) => MyAppState(), child: MaterialApp( title: 'Namer App', theme: ThemeData( useMaterial3: true, colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange), ), home: MyHomePage(), ), ); } } class MyAppState extends ChangeNotifier { var current = WordPair.random(); } class MyHomePage extends StatelessWidget { @override Widget build(BuildContext context) { var appState = context.watch<MyAppState>(); return Scaffold( body: Column( children: [ Text('A random idea:'), Text(appState.current.asLowerCase), ], ), ); } }
Menambahkan tombol
Selagi lib/main.dart terbuka, temukan tombol "run and debug" di pojok kanan atas jendela VS Code lalu klik tombol tersebut.
Setelah beberapa saat, aplikasi Anda diluncurkan dalam mode
debug. Tampilannya masih terlihat biasa saja:
Menambahkan tombol
Berikutnya, tambahkan tombol di bagian bawah Column, tepat di bawah instance Text kedua.
// File lib/main.dart // ... return Scaffold( body: Column( children: [ Text('A random AWESOME idea:'), Text(appState.current.asLowerCase), // ↓ Add this. ElevatedButton( onPressed: () { print('button pressed!'); }, child: Text('Next'), ), ], ), ); // ...
Saat kita menyimpan perubahan, aplikasi diperbarui kembali: Sebuah tombol
muncul dan, saat tombol diklik, Konsol Debug di VS Code menampilkan pesan
button pressed!.
Modifikasi Pertama
Scroll ke MyAppState lalu tambahkan metode
getNext. Metode getNext() baru menetapkan ulang
current dengan WordPair acak baru. Metode ini
juga memanggil notifyListeners()(metode ChangeNotifier) yang memastikan bahwa semua orang yang melihat
MyAppState diberi tahu.
// File lib/main.dart // ... class MyAppState extends ChangeNotifier { var current = WordPair.random(); // ↓ Add this. void getNext() { current = WordPair.random(); notifyListeners(); } } // ...
Tindakan terakhir adalah memanggil metode getNext dari
callback tombol tersebut.
// File lib/main.dart // ... ElevatedButton( onPressed: () { appState.getNext(); // ← This instead of print(). }, child: Text('Next'), ), // ...
Tulis ulang widget MyHomePage untuk
mengekstrak widget dengan kode berikut.
// File lib/main.dart // ... class MyHomePage extends StatelessWidget { @override Widget build(BuildContext context) { var appState = context.watch<MyAppState>(); var pair = appState.current; // ← Add this. return Scaffold( body: Column( children: [ Text('A random AWESOME idea:'), Text(pair.asLowerCase), // ← Change to this. ElevatedButton( onPressed: () { appState.getNext(); }, child: Text('Next'), ), ], ), ); } } // ...
Bagus. Widget Text tidak lagi merujuk kepada keseluruhan
appState. Sekarang, panggil menu Refactor. Pada VS Code, kita melakukan ini melalui salah satu dari dua cara:
- Klik kanan potongan kode yang ingin difaktorkan ulang (dalam hal ini Text) dan pilih Refactor... dari menu drop-down, ATAU
-
Pindahkan kursor ke potongan kode yang ingin difaktorkan ulang (dalam
hal ini Text), lalu tekan Ctrl+. (Win/Linux) atau
Cmd+. (Mac).
Pada menu Refactor, pilih Extract Widget. Tetapkan nama, seperti BigCard, lalu klik Enter. Tindakan ini secara otomatis membuat class baru, BigCard, di akhir file saat ini. Class tersebut akan terlihat seperti berikut:
// File lib/main.dart // ... class BigCard extends StatelessWidget { const BigCard({ super.key, required this.pair, }); final WordPair pair; @override Widget build(BuildContext context) { return Text(pair.asLowerCase); } } // ...
Menambahkan Card
Temukan class BigCard dan metode
build() yang berada di dalamnya. Sama seperti sebelumnya,
panggil menu Refactor pada widget Text. Namun, kali ini kita tidak akan mengekstrak widget. Sebagai gantinya,
pilih Wrap with Padding. Tindakan ini menciptakan widget induk baru di sekitar widget
Text bernama Padding. Setelah menyimpannya, kita akan melihat bahwa kata acak tersebut
telah memiliki ruang yang lebih luas.
Tingkatkan padding dari nilai default 8.0. Misalnya, gunakan 20 untuk padding yang
lebih luas. Berikutnya, mari kita naik satu tingkat lebih tinggi.
Tempatkan kursor pada widget Padding, buka menu Refactor, lalu pilih Wrap with widget....
Tindakan ini memungkinkan kita untuk menentukan widget induk. Ketik "Card" dan tekan Enter.
Kode ini menggabungkan widget Padding, dan juga Text, dengan widget Card.
Menambahkan Fungsi
Scroll ke MyAppState dan tambahkan kode berikut:
// File lib/main.dart // ... class MyAppState extends ChangeNotifier { var current = WordPair.random(); void getNext() { current = WordPair.random(); notifyListeners(); } // ↓ Add the code below. var favorites = <WordPair>[]; void toggleFavorite() { if (favorites.contains(current)) { favorites.remove(current); } else { favorites.add(current); } notifyListeners(); } } // ...
Menambahkan tombol
-
Gabungkan tombol yang ada pada Row. Buka metode build() MyHomePage, letakkan kursor pada ElevatedButton, buka menu Refactor dengan Ctrl+. atau Cmd+., lalu
pilih Wrap with Row.
// File lib/main.dart // ... class MyHomePage extends StatelessWidget { @override Widget build(BuildContext context) { var appState = context.watch<MyAppState>(); var pair = appState.current; return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ BigCard(pair: pair), SizedBox(height: 10), Row( mainAxisSize: MainAxisSize.min, // ← Add this. children: [ ElevatedButton( onPressed: () { appState.getNext(); }, child: Text('Next'), ), ], ), ], ), ), ); } } // ...
-
Tambahkan tombol Like dan hubungkan ke
toggleFavorite()
// File lib/main.dart // ... class MyHomePage extends StatelessWidget { @override Widget build(BuildContext context) { var appState = context.watch<MyAppState>(); var pair = appState.current; // ↓ Add this. IconData icon; if (appState.favorites.contains(pair)) { icon = Icons.favorite; } else { icon = Icons.favorite_border; } return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ BigCard(pair: pair), SizedBox(height: 10), Row( mainAxisSize: MainAxisSize.min, children: [ // ↓ And this. ElevatedButton.icon( onPressed: () { appState.toggleFavorite(); }, icon: Icon(icon), label: Text('Like'), ), SizedBox(width: 10), ElevatedButton( onPressed: () { appState.getNext(); }, child: Text('Next'), ), ], ), ], ), ), ); } } // ...
Aplikasi akan terlihat seperti berikut:
Menambahkan kolom samping navigasi
Kita akan membuat layar terpisah untuk bagian favorit pengguna
dengan StatefulWidget. Untuk
mencapai langkah ini, pisahkan MyHomePage menjadi 2
widget terpisah. Pilih keseluruhan MyHomePage, hapus, dan gantikan dengan kode berikut:
// File lib/main.dart // ... class MyHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Row( children: [ SafeArea( child: NavigationRail( extended: false, destinations: [ NavigationRailDestination( icon: Icon(Icons.home), label: Text('Home'), ), NavigationRailDestination( icon: Icon(Icons.favorite), label: Text('Favorites'), ), ], selectedIndex: 0, onDestinationSelected: (value) { print('selected: $value'); }, ), ), Expanded( child: Container( color: Theme.of(context).colorScheme.primaryContainer, child: GeneratorPage(), ), ), ], ), ); } } class GeneratorPage extends StatelessWidget { @override Widget build(BuildContext context) { var appState = context.watch<MyAppState>(); var pair = appState.current; IconData icon; if (appState.favorites.contains(pair)) { icon = Icons.favorite; } else { icon = Icons.favorite_border; } return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ BigCard(pair: pair), SizedBox(height: 10), Row( mainAxisSize: MainAxisSize.min, children: [ ElevatedButton.icon( onPressed: () { appState.toggleFavorite(); }, icon: Icon(icon), label: Text('Like'), ), SizedBox(width: 10), ElevatedButton( onPressed: () { appState.getNext(); }, child: Text('Next'), ), ], ), ], ), ); } } // ...
setState
Widget stateful baru hanya perlu melacak satu variabel:
selectedIndex. Buat 3 perubahan berikut untuk _MyHomePageState
// File lib/main.dart // ... class _MyHomePageState extends State<MyHomePage> { var selectedIndex = 0; // ← Add this property. @override Widget build(BuildContext context) { return Scaffold( body: Row( children: [ SafeArea( child: NavigationRail( extended: false, destinations: [ NavigationRailDestination( icon: Icon(Icons.home), label: Text('Home'), ), NavigationRailDestination( icon: Icon(Icons.favorite), label: Text('Favorites'), ), ], selectedIndex: selectedIndex, // ← Change to this. onDestinationSelected: (value) { // ↓ Replace print with this. setState(() { selectedIndex = value; }); }, ), ), Expanded( child: Container( color: Theme.of(context).colorScheme.primaryContainer, child: GeneratorPage(), ), ), ], ), ); } } // ...
// File lib/main.dart // ... class _MyHomePageState extends State<MyHomePage> { var selectedIndex = 0; @override Widget build(BuildContext context) { Widget page; switch (selectedIndex) { case 0: page = GeneratorPage(); break; case 1: page = Placeholder(); break; default: throw UnimplementedError('no widget for $selectedIndex'); } return LayoutBuilder(builder: (context, constraints) { return Scaffold( body: Row( children: [ SafeArea( child: NavigationRail( extended: constraints.maxWidth >= 600, // ← Here. destinations: [ NavigationRailDestination( icon: Icon(Icons.home), label: Text('Home'), ), NavigationRailDestination( icon: Icon(Icons.favorite), label: Text('Favorites'), ), ], selectedIndex: selectedIndex, onDestinationSelected: (value) { setState(() { selectedIndex = value; }); }, ), ), Expanded( child: Container( color: Theme.of(context).colorScheme.primaryContainer, child: page, ), ), ], ), ); }); } } // ...
Menambahkan halaman baru
Berikut class FavoritesPage baru:
// File lib/main.dart // ... class FavoritesPage extends StatelessWidget { @override Widget build(BuildContext context) { var appState = context.watch<MyAppState>(); if (appState.favorites.isEmpty) { return Center( child: Text('No favorites yet.'), ); } return ListView( children: [ Padding( padding: const EdgeInsets.all(20), child: Text('You have ' '${appState.favorites.length} favorites:'), ), for (var pair in appState.favorites) ListTile( leading: Icon(Icons.favorite), title: Text(pair.asLowerCase), ), ], ); } }
Komentar
Posting Komentar