Belajar Membuat Aplikasi Tempat Wisata Dengan Flutter : Bagian Kedua


Belajar Membuat Aplikasi Tempat Wisata Dengan Flutter : Bagian Kedua - Setelah mempelajari beberapa materi tambahan, sekarang saatnya kita melanjutkan project aplikasi wisata kita. Pada codelab ini kita akan membuat aplikasi dengan tampilan seperti berikut:

2020061515084944106af6c78e2c9b7a17877d21d36d61.jpeg
  1. Mari kita mulai dengan membuka dan melanjutkan codelab kita sebelumnya.
  2. Untuk memudahkan dalam membaca sekaligus merapikan kode, mari kita pindahkan widget atau kelas DetailScreen ke sebuah file dart baru. Anda dapat membuat file baru dengan cara klik kanan pada folder lib -> New -> Dart File. Berikan nama detail_screen.dart.
    20200615150957fd8e43b29beb5265379ca431e7fe9bc9.jpeg
  3. Anda akan mendapati beberapa eror akibat adanya library atau package yang belum terpasang. Pada file detail_screen.dart tambahkan kode import berikut di baris paling atas untuk menggunakan package material design di dalam file.


    1. import 'package:flutter/material.dart';


  4. Selanjutnya karena kita akan menggunakan file widget DetailScreen di file main.dart, maka kita juga perlu melakukan import.


    1. import 'package:wisatabandung/detail_screen.dart';


  5. Kemudian kita akan menambahkan sebuah gambar ke tampilan paling atas halaman. Gambar ini akan kita ambil dari asset. Untuk itu, kita perlu menambahkan berkas yang ingin ditampilkan ke dalam project dan menambahkannya pada file pubspec.yaml. Aset gambar dapat Anda unduh pada tautan berikut.


    1. flutter:  

    2. uses-material-design: true

    3. assets:

    4.     - images/


    Tambahkan widget Image di child paling atas dari Column.


    1. class DetailScreen extends StatelessWidget {

    2.   @override

    3.   Widget build(BuildContext context) {

    4.     return Scaffold(

    5.       backgroundColor: Colors.black,

    6.       body: Column(

    7.         crossAxisAlignment: CrossAxisAlignment.stretch,

    8.         children: <Widget>[

    9.           Image.asset('images/farm-house.jpg'),

    10.           Container(...),

    11.           Container(...),

    12.           Container(...),

    13.         ],

    14.       ),

    15.     );

    16.   }

    17. }


    Jalankan aplikasi Anda untuk melihat perubahan.
  6. Selanjutnya kita akan menampilkan beberapa gambar lagi di bagian bawah. Kali ini kita akan mengambil gambar melalui url. Mari kita mulai dengan satu gambar terlebih dahulu.
  1. class DetailScreen extends StatelessWidget {
  2.   @override
  3.   Widget build(BuildContext context) {
  4.     return Scaffold(
  5.       backgroundColor: Colors.black,
  6.       body: Column(
  7.         crossAxisAlignment: CrossAxisAlignment.stretch,
  8.         children: <Widget>[
  9.           Image.asset('images/farm-house.jpg'),
  10.           Container(...),
  11.           Container(...),
  12.           Container(...),
  13.           Image.network(
  14. 'https://media-cdn.tripadvisor.com/media/photo-s/0d/7c/59/70/farmhouse-lembang.jpg')
  15.         ],
  16.       ),
  17.     );
  18.   }
  19. }

  1. Apabila gambar yang kita tampilkan terlalu besar sementara layar pada perangkat terlalu kecil, maka akan terlihat tampilan garis hitam-kuning yang menunjukkan terjadi overflow. Kondisi overflow ini terjadi ketika konten yang kita tampilkan melebihi luas layar yang ada.
    20200615152909af5065c8b65886e8c21200e2c2c3396f.jpeg
  2. Sebagai solusi, tentunya kita bisa mengubah ukuran dari gambar, namun tentunya tidak praktis jika kita harus mengubah ukuran setiap gambar yang ditampilkan. Tentu ada banyak sekali ukuran layar yang tersedia, bukan? Solusi lainnya yaitu dengan menerapkan scrolling. Salah satu widget scrolling yang bisa kita manfaatkan adalah SingleChildScrollView. Widget ini membutuhkan satu child yang nantinya bisa di-scroll pada layar. Pindahkan widget Column ke dalam SingleChildScrollView supaya nantinya bisa di-scroll.
  1. class DetailScreen extends StatelessWidget {
  2.   @override
  3.   Widget build(BuildContext context) {
  4.     return Scaffold(
  5.       backgroundColor: Colors.black,
  6.       body: SingleChildScrollView(
  7.         child: Column(
  8.           crossAxisAlignment: CrossAxisAlignment.stretch,
  9.           children: <Widget>[
  10.             Image.asset('images/farm-house.jpg'),
  11.             Container(...),
  12.             Container(...),
  13.             Container(...),
  14.             Image.network(
  15.                 'https://media-cdn.tripadvisor.com/media/photo-s/0d/7c/59/70/farmhouse-lembang.jpg')
  16.           ],
  17.         ),
  18.       ),
  19.     );
  20.   }
  21. }


  1. Kemudian jalankan hot reload. Seharusnya masalah overflow sudah teratasi dengan adanya scrolling.
  2. Selanjutnya kita akan menambahkan beberapa gambar lagi yang disusun secara horizontal. Anda mungkin mengira untuk menggunakan widget Row supaya gambar bisa tersusun secara horizontal. Namun, perlu diingat bahwa kita juga memerlukan fitur scrolling agar tidak terjadi overflow. Oleh karena itu, kita akan menggunakan ListView. Widget ini memungkinkan kita untuk menerapkan scrolling terhadap beberapa item (children).

  1. class DetailScreen extends StatelessWidget {
  2.   @override
  3.   Widget build(BuildContext context) {
  4.     return Scaffold(
  5.       backgroundColor: Colors.black,
  6.       body: SingleChildScrollView(
  7.         child: Column(
  8.           crossAxisAlignment: CrossAxisAlignment.stretch,
  9.           children: <Widget>[
  10.             Image.asset('images/farm-house.jpg'),
  11.             Container(...),
  12.             Container(...),
  13.             Container(...),
  14.             ListView(
  15.               children: <Widget>[
  16.                 Image.network(
  17.                     'https://media-cdn.tripadvisor.com/media/photo-s/0d/7c/59/70/farmhouse-lembang.jpg'),
  18.                 Image.network(
  19.                     'https://media-cdn.tripadvisor.com/media/photo-w/13/f0/22/f6/photo3jpg.jpg'),
  20.                 Image.network(
  21.                     'https://media-cdn.tripadvisor.com/media/photo-m/1280/16/a9/33/43/liburan-di-farmhouse.jpg'),
  22.               ],
  23. ),
  24.           ],
  25.         ),
  26.       ),
  27.     );
  28.   }
  29. }


Jika Anda menjalankan aplikasi atau melakukan hot reload, aplikasi Anda akan menjadi blank dan muncul pesan eror pada log. 

Kenapa ya? ListView diletakkan di dalam Column, di mana keduanya sama-sama memiliki atribut height yang memakan space di sepanjang layar. 

Sebagai solusi kita perlu memberikan ukuran tinggi yang statis terhadap ListView. 

Namun ListView tidak memiliki parameter height, lantas bagaimana nih? Caranya, gunakan widget lain yang memiliki parameter height. 

Anda dapat membungkus widget ListView ke dalam Container atau pun SizedBox. Ukuran tinggi ini nantinya juga digunakan sebagai tinggi Image yang tampil.


  1. class DetailScreen extends StatelessWidget {
  2.   @override
  3.   Widget build(BuildContext context) {
  4.     return Scaffold(
  5.       backgroundColor: Colors.black,
  6.       body: SingleChildScrollView(
  7.         child: Column(
  8.           crossAxisAlignment: CrossAxisAlignment.stretch,
  9.           children: <Widget>[
  10.             Image.asset('images/farm-house.jpg'),
  11.             Container(...),
  12.             Container(...),
  13.             Container(...),
  14.             Container(
  15.               height: 150,
  16. child: ListView(
  17.                 children: <Widget>[
  18.                   Image.network(
  19.                       'https://media-cdn.tripadvisor.com/media/photo-s/0d/7c/59/70/farmhouse-lembang.jpg'),
  20.                   Image.network(
  21.                       'https://media-cdn.tripadvisor.com/media/photo-w/13/f0/22/f6/photo3jpg.jpg'),
  22.                   Image.network(
  23.                       'https://media-cdn.tripadvisor.com/media/photo-m/1280/16/a9/33/43/liburan-di-farmhouse.jpg'),
  24.                 ],
  25.               ),
  26.             ),
  27.           ],
  28.         ),
  29.       ),
  30.     );
  31.   }
  32. }


Secara default arah scroll dari ListView adalah vertikal. Untuk mengubahnya menjadi horizontal kita cukup menambahkan parameter scrollDirection bernilai Axis.horizontal.

  1. class DetailScreen extends StatelessWidget {
  2.   @override
  3.   Widget build(BuildContext context) {
  4.     return Scaffold(
  5.       backgroundColor: Colors.black,
  6.       body: SingleChildScrollView(
  7.         child: Column(
  8.           crossAxisAlignment: CrossAxisAlignment.stretch,
  9.           children: <Widget>[
  10.             Image.asset('images/farm-house.jpg'),
  11.             Container(...),
  12.             Container(...),
  13.             Container(...),
  14.             Container(
  15.               height: 150,
  16.               child: ListView(
  17.                 scrollDirection: Axis.horizontal,
  18.                 children: <Widget>[
  19.                   Image.network(
  20.                       'https://media-cdn.tripadvisor.com/media/photo-s/0d/7c/59/70/farmhouse-lembang.jpg'),
  21.                   Image.network(
  22.                       'https://media-cdn.tripadvisor.com/media/photo-w/13/f0/22/f6/photo3jpg.jpg'),
  23.                   Image.network(
  24.                       'https://media-cdn.tripadvisor.com/media/photo-m/1280/16/a9/33/43/liburan-di-farmhouse.jpg'),
  25.                 ],
  26.               ),
  27.             ),
  28.           ],
  29.         ),
  30.       ),
  31.     );
  32.   }
  33. }



Selanjutnya, kita akan sedikit merapikan tampilan gambar supaya terlihat lebih rapi dan menarik. Tambahkan Padding pada masing-masing Image supaya antar gambar tidak terlalu rapat.


  1. Container(
  2.   height: 150,
  3.   child: ListView(
  4.     scrollDirection: Axis.horizontal,
  5.     children: <Widget>[
  6.       Padding(
  7.         padding: const EdgeInsets.all(4.0),
  8.         child: Image.network(         'https://media-cdn.tripadvisor.com/media/photo-s/0d/7c/59/70/farmhouse-lembang.jpg'),
  9.       ),
  10.       Padding(
  11.         padding: const EdgeInsets.all(4.0),
  12.         child: Image.network(               'https://media-cdn.tripadvisor.com/media/photo-w/13/f0/22/f6/photo3jpg.jpg'),
  13.       ),
  14.       Padding(
  15.        padding: const EdgeInsets.all(4.0),
  16.        child: Image.network(                      'https://media-cdn.tripadvisor.com/media/photo-m/1280/16/a9/33/43/liburan-di-farmhouse.jpg'),
  17. ),
  18.     ],
  19.   ),
  20. ),




  1. Bagaimana membuat gambar memiliki sudut yang membulat seperti pada contoh? Sekali lagi, dokumentasi adalah sahabat terbaik Anda dalam mengembangkan aplikasi Flutter. Anda dapat memanfaatkan mesin pencari untuk menemukan widget sesuai keinginan. Misalnya, dengan memanfaatkan Google Anda dapat menemukan bahwa ada widget yang memungkinkan gambar memiliki radius, yaitu ClipRRect. Masukkan widget Image Anda sebagai child dari ClipRRect dan berikan borderRadius, maka Anda akan mendapatkan Image dengan sudut yang tak bersiku.
    202006151556576c81ea5b35991396fa76c627105ee064.jpeg
  2. Terakhir, kita akan menggunakan custom Font. Anda bebas menggunakan font kesukaan Anda. Pada contoh ini akan menggunakan font Staatliches dan Oxygen. Tambahkan font yang akan digunakan ke dalam project dan daftarkan pada pubscpec.yaml.


    1. fonts:

    2.     - family: Staatliches

    3.       fonts:

    4.         - asset: fonts/Staatliches-Regular.ttf

    5.     - family: Oxygen

    6.       fonts:

    7.         - asset: fonts/Oxygen-Regular.ttf


  3. Tambahkan parameter fontFamily pada widget TextStyle untuk menerapkan style pada Text.


    1. Container(

    2.   margin: EdgeInsets.only(top: 16.0),

    3.   child: Text(

    4.     'Farm House Lembang',

    5.     textAlign: TextAlign.center,

    6.     style: TextStyle(

    7.       fontSize: 30.0,

    8.       fontFamily: 'Staatliches',

    9.     ),

    10.   ),

    11. ),


  4. Jika Anda memiliki beberapa teks dengan style yang sama, Anda dapat menggunakan variabel untuk menyimpan TextStyledan meringkas kode.


    1. var informationTextStyle = TextStyle(

    2.   fontFamily: 'Oxygen',

    3. );


    Gunakan variabel tersebut pada masing-masing widgetyang membutuhkan.
  1. children: <Widget>[
  2.   Column(
  3.     children: <Widget>[
  4.       Icon(Icons.calendar_today),
  5.       SizedBox(height: 8.0),
  6.       Text(
  7.         'Open Everyday',
  8.         style: informationTextStyle,
  9.       ),
  10.     ],
  11.   ),
  12.   Column(
  13.     children: <Widget>[
  14.       Icon(Icons.access_time),
  15.       SizedBox(height: 8.0),
  16.       Text(
  17.         '09:00 - 20:00',
  18.         style: informationTextStyle,
  19.       ),
  20.     ],
  21.   ),
  22.   Column(
  23.     children: <Widget>[
  24.       Icon(Icons.monetization_on),
  25.       SizedBox(height: 8.0),
  26.       Text(
  27.         'Rp 25.000',
  28.         style: informationTextStyle,
  29.       ),
  30.     ],
  31.   ),
  32. ],


  1. Jalankan aplikasi untuk melihat hasil akhir dari codelab ini.
    20200615160254464607c29f540de92c1ac172929b01a4.jpeg
  2. Seluruh kodenya adalah seperti berikut
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter/widgets.dart';
  3.  
  4.  
  5. var informationTextStyle = TextStyle(
  6.   fontFamily: 'Oxygen',
  7. );
  8.  
  9.  
  10. class DetailScreen extends StatelessWidget {
  11.   @override
  12.   Widget build(BuildContext context) {
  13.     return Scaffold(
  14.       backgroundColor: Colors.black,
  15.       body: SingleChildScrollView(
  16.         child: Column(
  17.           crossAxisAlignment: CrossAxisAlignment.stretch,
  18.           children: <Widget>[
  19.             Image.asset('images/farm-house.jpg'),
  20.             Container(
  21.               margin: EdgeInsets.only(top: 16.0),
  22.               child: Text(
  23.                 'Farm House Lembang',
  24.                 textAlign: TextAlign.center,
  25.                 style: TextStyle(
  26.                   fontSize: 30.0,
  27.                   fontFamily: 'Staatliches',
  28.                 ),
  29.               ),
  30.             ),
  31.             Container(
  32.               margin: EdgeInsets.symmetric(vertical: 16.0),
  33.               child: Row(
  34.                 mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  35.                 children: <Widget>[
  36.                   Column(
  37.                     children: <Widget>[
  38.                       Icon(Icons.calendar_today),
  39.                       SizedBox(height: 8.0),
  40.                       Text(
  41.                         'Open Everyday',
  42.                         style: informationTextStyle,
  43.                       ),
  44.                     ],
  45.                   ),
  46.                   Column(
  47.                     children: <Widget>[
  48.                       Icon(Icons.access_time),
  49.                       SizedBox(height: 8.0),
  50.                       Text(
  51.                         '09:00 - 20:00',
  52.                         style: informationTextStyle,
  53.                       ),
  54.                     ],
  55.                   ),
  56.                   Column(
  57.                     children: <Widget>[
  58.                       Icon(Icons.monetization_on),
  59.                       SizedBox(height: 8.0),
  60.                       Text(
  61.                         'Rp 25.000',
  62.                         style: informationTextStyle,
  63.                       ),
  64.                     ],
  65.                   ),
  66.                 ],
  67.               ),
  68.             ),
  69.             Container(
  70.               padding: EdgeInsets.all(16.0),
  71.               child: Text(
  72.                 'Berada di jalur utama Bandung-Lembang, Farm House menjadi objek wisata yang tidak pernah sepi pengunjung. Selain karena letaknya strategis, kawasan ini juga menghadirkan nuansa wisata khas Eropa. Semua itu diterapkan dalam bentuk spot swafoto Instagramable.',
  73.                 textAlign: TextAlign.center,
  74.                 style: TextStyle(
  75.                   fontSize: 16.0,
  76.                   fontFamily: 'Oxygen',
  77.                 ),
  78.               ),
  79.             ),
  80.             Container(
  81.               height: 150,
  82.               child: ListView(
  83.                 scrollDirection: Axis.horizontal,
  84.                 children: <Widget>[
  85.                   Padding(
  86.                     padding: const EdgeInsets.all(4.0),
  87.                     child: ClipRRect(
  88.                       borderRadius: BorderRadius.circular(10),
  89.                       child: Image.network(
  90.                           'https://media-cdn.tripadvisor.com/media/photo-s/0d/7c/59/70/farmhouse-lembang.jpg'),
  91.                     ),
  92.                   ),
  93.                   Padding(
  94.                     padding: const EdgeInsets.all(4.0),
  95.                     child: ClipRRect(
  96.                       borderRadius: BorderRadius.circular(10),
  97.                       child: Image.network(
  98.                           'https://media-cdn.tripadvisor.com/media/photo-w/13/f0/22/f6/photo3jpg.jpg'),
  99.                     ),
  100.                   ),
  101.                   Padding(
  102.                     padding: const EdgeInsets.all(4.0),
  103.                     child: ClipRRect(
  104.                       borderRadius: BorderRadius.circular(10),
  105.                       child: Image.network(
  106.                           'https://media-cdn.tripadvisor.com/media/photo-m/1280/16/a9/33/43/liburan-di-farmhouse.jpg'),
  107.                     ),
  108.                   ),
  109.                 ],
  110.               ),
  111.             ),
  112.           ],
  113.         ),
  114.       ),
  115.     );
  116.   }
  117. }


Anda juga dapat mengunduh seluruh kodenya pada tautan berikut: https://github.com/dicodingacademy/a159-flutter-pemula-labs/tree/codelab2-final.



Komentar