Tugas 12 Flutter - Music Application
Nama : Helsa Nesta Dhaifullah
NRP : 5025201005Kelas : 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). Project pertama
untuk latihan Flutter adalah membuat sebuah aplikasi Musik MyArtist,
sebuah aplikasi pemutar musik tempat penggemar dapat terus mengikuti kabar
terbaru dari artis favoritnya. Kita akan membahas cara memodifikasi desain
aplikasi agar terlihat bagus di berbagai platform.
![]() |
Tampilan Desktop Aplikasi MyArtist |
![]() |
Tampilan Mobile Aplikasi MyArtist |
Mendapatkan Kode Awal
Sebagai starter aplikasi yang akan dibangun, kita dapat meng-clone repository Github Codelab dengan menjalankan perintah berikut.
git clone https://github.com/flutter/codelabs.git
cd codelabs/boring_to_beautiful/step_01/
Untuk memastikan aplikasi berfungsi dengan baik, coba jalankan aplikasi,
gunakan run & debug di VS Code.
Menerapkan Kode
File pertama yang akan kita modifikasi adalah "lib/src/features/home/view/home_screen.dart". Modifikasi kodenya dengan kode di bawah ini, sehingga file tersebut dapat
mengimpor material.dart dan menerapkan
widget stateful menggunakan dua class:
- pernyataan import akan menyediakan Komponen Material;
- class HomeScreen merepresentasikan seluruh halaman yang ditampilkan;
- metode build() class _HomeScreenState akan membuat root hierarki widget, yang memengaruhi cara pembuatan semua widget di UI
import 'package:adaptive_components/adaptive_components.dart'; import 'package:flutter/material.dart'; import '../../../shared/classes/classes.dart'; import '../../../shared/extensions.dart'; import '../../../shared/providers/providers.dart'; import '../../../shared/views/views.dart'; import '../../playlists/view/playlist_songs.dart'; import 'view.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({Key? key}) : super(key: key); @override State<HomeScreen> createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { @override Widget build(BuildContext context) { final PlaylistsProvider playlistProvider = PlaylistsProvider(); final List<Playlist> playlists = playlistProvider.playlists; final Playlist topSongs = playlistProvider.topSongs; final Playlist newReleases = playlistProvider.newReleases; final ArtistsProvider artistsProvider = ArtistsProvider(); final List<Artist> artists = artistsProvider.artists; return LayoutBuilder( builder: (context, constraints) { // Add conditional mobile layout return Scaffold( body: SingleChildScrollView( child: AdaptiveColumn( children: [ AdaptiveContainer( columnSpan: 12, child: Padding( padding: const EdgeInsets.all(2), // Modify this line child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text( 'Good morning', style: context.displaySmall, ), ), const SizedBox(width: 20), const BrightnessToggle(), ], ), ), ), AdaptiveContainer( columnSpan: 12, child: Column( children: [ const HomeHighlight(), LayoutBuilder( builder: (context, constraints) => HomeArtists( artists: artists, constraints: constraints, ), ), ], ), ), AdaptiveContainer( columnSpan: 12, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.all(2), // Modify this line child: Text( 'Recently played', style: context.headlineSmall, ), ), HomeRecent( playlists: playlists, ), ], ), ), AdaptiveContainer( columnSpan: 12, child: Padding( padding: const EdgeInsets.all(2), // Modify this line child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Flexible( flex: 10, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.all(2), // Modify this line child: Text( 'Top Songs Today', style: context.titleLarge, ), ), LayoutBuilder( builder: (context, constraints) => PlaylistSongs( playlist: topSongs, constraints: constraints, ), ), ], ), ), // Add spacer between tables Flexible( flex: 10, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.all(2), // Modify this line child: Text( 'New Releases', style: context.titleLarge, ), ), LayoutBuilder( builder: (context, constraints) => PlaylistSongs( playlist: newReleases, constraints: constraints, ), ), ], ), ), ], ), ), ), ], ), ), ); }, ); } }
Modifikasi Tipografi
Teks digunakan di banyak tempat dan penting untuk komunikasi dengan
pengguna. Apakah aplikasi kita ingin terlihat ramah dan menyenangkan atau
tepercaya dan profesional? Tampilan teks membentuk kesan pertama pengguna
tentang aplikasi kita.
-
Mulai dengan menambahkan isyarat visual sehingga pengguna dapat melihat
sekilas ikon utama dengan cepat untuk menemukan tab yang diinginkan.
Buka file "lib/src/shared/router.dart" tambahkan ikon utama yang berbeda untuk setiap tujuan navigasi
(beranda, playlist, dan artis):
const List<NavigationDestination> destinations = [ NavigationDestination( label: 'Home', icon: Icon(Icons.home), // Modify this line route: '/', ), NavigationDestination( label: 'Playlists', icon: Icon(Icons.playlist_add_check), // Modify this line route: '/playlists', ), NavigationDestination( label: 'Artists', icon: Icon(Icons.people), // Modify this line route: '/artists', ), ];
Hasil Penambahan Ikon pada Tab Navigasi Bar
-
Pilah jenis font dengan cermat. Setiap font mempunyai karakteristik
masing-masing. Untuk aplikasi musik, sebaiknya gunakan jenis font
sans-serif, seperti Montserrat, karena aplikasi musik ditujukan untuk memberikan kesan informal dan
menyenangkan.
Untuk menambahkan font Google ke aplikasi, jalankan perintah berikut di terminal VS Code. Tindakan ini juga memperbarui file pubspec untuk menambahkan font sebagai dependensi aplikasi.
$ flutter pub add google_fonts
import 'package:google_fonts/google_fonts.dart'; ... TextTheme get textTheme => GoogleFonts.montserratTextTheme(theme.textTheme);
untuk mengaktifkan perubahan. (Gunakan tombol di IDE Anda atau, dari command line, masukkan r untuk melakukan hot reload). Kalian akan melihat ikon NavigationRail baru beserta teks yang ditampilkan dalam font Montserrat.
Mengganti Tema Aplikasi
Tema membantu memberikan desain yang konsisten pada aplikasi dengan
menetapkan kumpulan warna dan gaya teks. Dengan tema, Anda bisa menerapkan
UI dengan cepat tanpa harus khawatir tentang detail kecil seperti memilih
warna yang tepat untuk setiap elemen.
-
Untuk menggunakan penyedia tema, buat instance dan teruskan ke objek
tema cakupan di MaterialApp, yang terletak di "lib/src/shared/app.dart". Tema akan diwarisi oleh objek
Theme bertingkat:
import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'playback/bloc/bloc.dart'; import 'providers/theme.dart'; import 'router.dart'; class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { final settings = ValueNotifier(ThemeSettings( sourceColor: Colors.pink, themeMode: ThemeMode.system, )); @override Widget build(BuildContext context) { return BlocProvider<PlaybackBloc>( create: (context) => PlaybackBloc(), child: DynamicColorBuilder( builder: (lightDynamic, darkDynamic) => ThemeProvider( lightDynamic: lightDynamic, darkDynamic: darkDynamic, settings: settings, child: NotificationListener<ThemeSettingChange>( onNotification: (notification) { settings.value = notification.settings; return true; }, child: ValueListenableBuilder<ThemeSettings>( valueListenable: settings, builder: (context, value, _) { final theme = ThemeProvider.of(context); // Add this line return MaterialApp.router( debugShowCheckedModeBanner: false, title: 'Flutter Demo', theme: theme.light(settings.value.sourceColor), // Add this line routeInformationParser: appRouter.routeInformationParser, routerDelegate: appRouter.routerDelegate, ); }, ), )), ), ); } }
- Untuk memilih warna aplikasi, buka Material Theme Builder dan coba berbagai warna untuk UI aplikasi Anda. Pastikan untuk memilih warna yang sesuai dengan estetika merek dan/atau preferensi pribadi Anda.
-
Teruskan nilai hex warna primer ke penyedia tema. Misalnya, warna
heksadesimal #00cbe6 ditentukan sebagai
Color(0xff00cbe6). ThemeProvider akan menghasilkan
ThemeData yang berisi kumpulan warna pelengkap yang Anda
lihat di Builder Tema Material:
final settings = ValueNotifier(ThemeSettings( sourceColor: Color(0xff00cbe6), // Replace this color themeMode: ThemeMode.system, ));
final colors = Theme.of(context).colorScheme;
-
Untuk menggunakan warna tertentu, akses peran warna di
colorScheme. Buka "lib/src/shared/views/outlined_card.dart" dan beri OutlinedCard batas
tepi:
class _OutlinedCardState extends State<OutlinedCard> { @override Widget build(BuildContext context) { return MouseRegion( cursor: widget.clickable ? SystemMouseCursors.click : SystemMouseCursors.basic, child: Container( child: widget.child, // Add from here... decoration: BoxDecoration( border: Border.all( color: Theme.of(context).colorScheme.outline, width: 1, ), ), // ... To here. ), ); } }
-
Pengguna dapat menyesuaikan kecerahan aplikasi di setelan sistem
perangkat. Di "lib/src/shared/app.dart", jika perangkat disetel ke mode gelap, tampilkan tema gelap dan
mode tema ke MaterialApp.
return MaterialApp.router( debugShowCheckedModeBanner: false, title: 'Flutter Demo', theme: theme.light(settings.value.sourceColor), darkTheme: theme.dark(settings.value.sourceColor), // Add this line themeMode: theme.themeMode(), // Add this line routeInformationParser: appRouter.routeInformationParser, routerDelegate: appRouter.routerDelegate, );
Menambahkan Desain Adaptif
Dengan Flutter, Anda bisa membuat aplikasi yang bekerja di berbagai
perangkat. Namun, aplikasi tidak selalu berperilaku sama di semua layar.
Pengguna mengharapkan fitur yang berbeda di setiap platform. Oleh karena
itu, untuk membuat aplikasi musik menjadi aplikasi multi-platform, Anda
perlu memodifikasi aplikasi musik agar lebih adaptif.
-
File "lib/src/shared/views/adaptive_navigation.dart" memiliki kelas navigasi untuk daftar tujuan dan konten. Tata
letak dasar ini digunakan di berbagai layar. Kolom samping navigasi
cocok untuk desktop dan layar besar, sementara di perangkat seluler,
gunakan menu navigasi bawah.
import 'package:flutter/material.dart'; class AdaptiveNavigation extends StatelessWidget { const AdaptiveNavigation({ Key? key, required this.destinations, required this.selectedIndex, required this.onDestinationSelected, required this.child, }) : super(key: key); final List<NavigationDestination> destinations; final int selectedIndex; final void Function(int index) onDestinationSelected; final Widget child; @override Widget build(BuildContext context) { return LayoutBuilder( builder: (context, dimens) { // Tablet Layout if (dimens.maxWidth >= 600) { // Add this line return Scaffold( body: Row( children: [ NavigationRail( extended: dimens.maxWidth >= 800, minExtendedWidth: 180, destinations: destinations .map((e) => NavigationRailDestination( icon: e.icon, label: Text(e.label), )) .toList(), selectedIndex: selectedIndex, onDestinationSelected: onDestinationSelected, ), Expanded(child: child), ], ), ); } // Add this line // Mobile Layout // Add from here... return Scaffold( body: child, bottomNavigationBar: NavigationBar( destinations: destinations, selectedIndex: selectedIndex, onDestinationSelected: onDestinationSelected, ), ); // ... To here. }, ); } }
-
Agar aplikasi Anda responsif, gunakan titik henti adaptif untuk
mengubah tata letak sesuai ukuran layar. Layar kecil tidak bisa
menampilkan konten layar besar tanpa mengecilkannya, jadi buat tata
letak khusus seluler dengan tab untuk membagi konten. Mulailah
dengan metode ekstensi di "lib/src/shared/extensions.dart" dalam project MyArtist untuk desain yang
dioptimalkan bagi berbagai perangkat.
extension BreakpointUtils on BoxConstraints { bool get isTablet => maxWidth > 730; bool get isDesktop => maxWidth > 1200; bool get isMobile => !isTablet && !isDesktop; }
-
Tata letak adaptif memerlukan dua tata letak: satu untuk perangkat
seluler dan tata letak responsif untuk layar yang lebih besar.
LayoutBuilder saat ini hanya menampilkan tata letak
desktop. Di "lib/src/features/home/view/home_screen.dart", buat tata letak seluler sebagai TabBar dan
TabBarView dengan 4 tab.
import 'package:adaptive_components/adaptive_components.dart'; import 'package:flutter/material.dart'; import '../../../shared/classes/classes.dart'; import '../../../shared/extensions.dart'; import '../../../shared/providers/providers.dart'; import '../../../shared/views/views.dart'; import '../../playlists/view/playlist_songs.dart'; import 'view.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({Key? key}) : super(key: key); @override State<HomeScreen> createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { @override Widget build(BuildContext context) { final PlaylistsProvider playlistProvider = PlaylistsProvider(); final List<Playlist> playlists = playlistProvider.playlists; final Playlist topSongs = playlistProvider.topSongs; final Playlist newReleases = playlistProvider.newReleases; final ArtistsProvider artistsProvider = ArtistsProvider(); final List<Artist> artists = artistsProvider.artists; return LayoutBuilder( builder: (context, constraints) { // Add from here... if (constraints.isMobile) { return DefaultTabController( length: 4, child: Scaffold( appBar: AppBar( centerTitle: false, title: const Text('Good morning'), actions: const [BrightnessToggle()], bottom: const TabBar( isScrollable: true, tabs: [ Tab(text: 'Home'), Tab(text: 'Recently Played'), Tab(text: 'New Releases'), Tab(text: 'Top Songs'), ], ), ), body: LayoutBuilder( builder: (context, constraints) => TabBarView( children: [ SingleChildScrollView( child: Column( children: [ const HomeHighlight(), HomeArtists( artists: artists, constraints: constraints, ), ], ), ), HomeRecent( playlists: playlists, axis: Axis.vertical, ), PlaylistSongs( playlist: topSongs, constraints: constraints, ), PlaylistSongs( playlist: newReleases, constraints: constraints, ), ], ), ), ), ); } // ... To here. return Scaffold( body: SingleChildScrollView( child: AdaptiveColumn( children: [ AdaptiveContainer( columnSpan: 12, child: Padding( padding: const EdgeInsets.symmetric( horizontal: 20, vertical: 40, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text( 'Good morning', style: context.displaySmall, ), ), const SizedBox(width: 20), const BrightnessToggle(), ], ), ), ), AdaptiveContainer( columnSpan: 12, child: Column( children: [ const HomeHighlight(), LayoutBuilder( builder: (context, constraints) => HomeArtists( artists: artists, constraints: constraints, ), ), ], ), ), AdaptiveContainer( columnSpan: 12, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric( horizontal: 15, vertical: 20, ), child: Text( 'Recently played', style: context.headlineSmall, ), ), HomeRecent( playlists: playlists, ), ], ), ), AdaptiveContainer( columnSpan: 12, child: Padding( padding: const EdgeInsets.all(15), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Flexible( flex: 10, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only(left: 8, bottom: 8), child: Text( 'Top Songs Today', style: context.titleLarge, ), ), LayoutBuilder( builder: (context, constraints) => PlaylistSongs( playlist: topSongs, constraints: constraints, ), ), ], ), ), const SizedBox(width: 25), Flexible( flex: 10, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only(left: 8, bottom: 8), child: Text( 'New Releases', style: context.titleLarge, ), ), LayoutBuilder( builder: (context, constraints) => PlaylistSongs( playlist: newReleases, constraints: constraints, ), ), ], ), ), ], ), ), ), ], ), ), ); }, ); } }
Tampilan Home Screen dengan 4 Tab Hasil Modifikasi
-
Atur jarak konten di "lib/src/features/home/view/home_screen.dart" sehingga lebih banyak ruang dan nyaman dilihat pengguna.
Scaffold( body: SingleChildScrollView( child: AdaptiveColumn( children: [ AdaptiveContainer( columnSpan: 12, child: Padding( padding: const EdgeInsets.fromLTRB(20, 25, 20, 10), // Modify this line child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text( 'Good morning', style: context.displaySmall, ), ), const SizedBox(width: 20), const BrightnessToggle(), ], ), ), ), AdaptiveContainer( columnSpan: 12, child: Column( children: [ const HomeHighlight(), LayoutBuilder( builder: (context, constraints) => HomeArtists( artists: artists, constraints: constraints, ), ), ], ), ), AdaptiveContainer( columnSpan: 12, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric( horizontal: 15, vertical: 10, ), // Modify this line child: Text( 'Recently played', style: context.headlineSmall, ), ), HomeRecent( playlists: playlists, ), ], ), ), AdaptiveContainer( columnSpan: 12, child: Padding( padding: const EdgeInsets.all(15), // Modify this line child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Flexible( flex: 10, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only(left: 8, bottom: 8), // Modify this line child: Text( 'Top Songs Today', style: context.titleLarge, ), ), LayoutBuilder( builder: (context, constraints) => PlaylistSongs( playlist: topSongs, constraints: constraints, ), ), ], ), ), const SizedBox(width: 25), Flexible( flex: 10, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only(left: 8, bottom: 8), // Modify this line child: Text( 'New Releases', style: context.titleLarge, ), ), LayoutBuilder( builder: (context, constraints) => PlaylistSongs( playlist: newReleases, constraints: constraints, ), ), ], ), ), ], ), ), ), ], ), ), );
-
Atur jarak konten di "lib/src/features/home/view/home_highlight.dart" juga.
class HomeHighlight extends StatelessWidget { const HomeHighlight({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Row( children: [ Expanded( child: Padding( // Modify this line padding: const EdgeInsets.symmetric(horizontal: 35, vertical: 5), child: Clickable( child: SizedBox( height: 275, child: ClipRRect( borderRadius: BorderRadius.circular(10), child: Image.asset( 'assets/images/news/concert.jpeg', fit: BoxFit.cover, ), ), ), onTap: () => launch('https://docs.flutter.dev'), ), ), ), ], ); } }
-
Lakukan hot reload pada aplikasi. Tata letak dan spasinya akan
terlihat jauh lebih baik. Untuk sentuhan akhir, tambahkan gerakan
dan animasi.
Tampilan Home Screen Setelah Ada Pengaturan Jarak Antar Konten
Menambahkan gerakan dan animasi
Menganimasikan transisi antar-layar
ThemeProvider menentukan
pageTransitionsTheme untuk platform seluler (iOS, Android), sementara untuk pengguna
desktop, aplikasi menggunakan masukan dari klik mouse atau trackpad,
sehingga animasi transisi tidak diperlukan. Flutter memungkinkan
pengaturan animasi transisi layar yang disesuaikan berdasarkan platform
yang ditargetkan, diimplementasikan dalam "lib/src/shared/providers/theme.dart".
final pageTransitionsTheme = const PageTransitionsTheme( builders: <TargetPlatform, PageTransitionsBuilder>{ TargetPlatform.android: FadeUpwardsPageTransitionsBuilder(), TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), TargetPlatform.linux: NoAnimationPageTransitionsBuilder(), TargetPlatform.macOS: NoAnimationPageTransitionsBuilder(), TargetPlatform.windows: NoAnimationPageTransitionsBuilder(), }, );
Kemudian, teruskan PageTransitionsTheme ke tema terang dan
gelap pada "lib/src/shared/providers/theme.dart".
ThemeData light([Color? targetColor]) { final _colors = colors(Brightness.light, targetColor); return ThemeData.light().copyWith( pageTransitionsTheme: pageTransitionsTheme, // Add this line colorScheme: ColorScheme.fromSeed( seedColor: source(targetColor), brightness: Brightness.light, ), appBarTheme: appBarTheme(_colors), cardTheme: cardTheme(), listTileTheme: listTileTheme(), tabBarTheme: tabBarTheme(_colors), scaffoldBackgroundColor: _colors.background, ); } ThemeData dark([Color? targetColor]) { final _colors = colors(Brightness.dark, targetColor); return ThemeData.dark().copyWith( pageTransitionsTheme: pageTransitionsTheme, // Add this line colorScheme: ColorScheme.fromSeed( seedColor: source(targetColor), brightness: Brightness.dark, ), appBarTheme: appBarTheme(_colors), cardTheme: cardTheme(), listTileTheme: listTileTheme(), tabBarTheme: tabBarTheme(_colors), scaffoldBackgroundColor: _colors.background, ); }
![]() |
Hasil Penambahan Animasi Transisi Antar Layar |
Menambahkan status pengarahan kursor
Salah satu cara untuk menambahkan interaksi ke aplikasi desktop adalah
dengan menggunakan pengarahan kursor, di mana widget dapat mengubah
status kursor seperti warna, bentuk, atau konten saat kursor mengarah ke
widget tersebut. Secara bawaan, dalam class
_OutlinedCardState yang digunakan untuk kartu playlist
"baru diputar", menggunakan MouseRegion untuk mengubah
ikon kursor menjadi pointer saat kursor mengarah ke kartu. Namun, Anda
dapat menambahkan lebih banyak elemen visual untuk meningkatkan
pengalaman interaksi tersebut.
-
Buka "lib/src/shared/views/outlined_card.dart" dan ganti kontennya dengan penerapan berikut untuk memperkenalkan
status _hovered.
import 'package:flutter/material.dart'; class OutlinedCard extends StatefulWidget { const OutlinedCard({ Key? key, required this.child, this.clickable = true, }) : super(key: key); final Widget child; final bool clickable; @override State<OutlinedCard> createState() => _OutlinedCardState(); } class _OutlinedCardState extends State<OutlinedCard> { bool _hovered = false; @override Widget build(BuildContext context) { final borderRadius = BorderRadius.circular(_hovered ? 20 : 8); const animationCurve = Curves.easeInOut; return MouseRegion( onEnter: (_) { if (!widget.clickable) return; setState(() { _hovered = true; }); }, onExit: (_) { if (!widget.clickable) return; setState(() { _hovered = false; }); }, cursor: widget.clickable ? SystemMouseCursors.click : SystemMouseCursors.basic, child: AnimatedContainer( duration: kThemeAnimationDuration, curve: animationCurve, decoration: BoxDecoration( border: Border.all( color: Theme.of(context).colorScheme.outline, width: 1, ), borderRadius: borderRadius, ), foregroundDecoration: BoxDecoration( color: Theme.of(context).colorScheme.onSurface.withOpacity( _hovered ? 0.12 : 0, ), borderRadius: borderRadius, ), child: TweenAnimationBuilder<BorderRadius>( duration: kThemeAnimationDuration, curve: animationCurve, tween: Tween(begin: BorderRadius.zero, end: borderRadius), builder: (context, borderRadius, child) => ClipRRect( clipBehavior: Clip.antiAlias, borderRadius: borderRadius, child: child, ), child: widget.child, ), ), ); } }
-
Lakukan hot reload pada aplikasi, lalu arahkan kursor ke salah satu
kartu playlist baru diputar.
Animasi pada Outline Card saat Kartu Dihover
-
Terakhir, animasikan nomor lagu pada playlist menjadi tombol putar
menggunakan widget HoverableSongPlayButton yang
ditentukan dalam "lib/src/shared/views/hoverable_song_play_button.dart". Di "lib/src/features/playlists/view/playlist_songs.dart", gabungkan widget Center (yang berisi nomor lagu)
dengan
HoverableSongPlayButton.
HoverableSongPlayButton( // Add this line hoverMode: HoverMode.overlay, // Add this line song: playlist.songs[index], // Add this line child: Center( // Modify this line child: Text( (index + 1).toString(), textAlign: TextAlign.center, ), ), ), // Add this line
-
Lakukan hot reload pada aplikasi, lalu arahkan kursor ke nomor lagu di playlist Lagu Teratas Hari Ini atau Rilis Baru. Angka akan berubah menjadi tombol putar yang memutar lagu saat Anda mengkliknya.
Animasi Tombol Putar saat Nomor Lagu Dihover
Mendapatkan Kode Solusi
Untuk mendapatkan kode solusi lengkap dari Latihan Flutter - modifikasi aplikasi MyArtist dapat di-download di link Github berikut.
https://github.com/helsanesta/tugas_flutter-MyArtist_App
Atau dapat langsung ke Github Codelab berikut.
https://github.com/helsanesta/tugas_flutter-MyArtist_App
Atau dapat langsung ke Github Codelab berikut.
Komentar
Posting Komentar