Praktikum Aplikasi Mobile 3 – Flutter

Input Widgets dan Basic Form

Lihat Laporan

Pendahuluan

Tujuan Praktikum

  1. Membuat beberapa input widgets
  2. Membuat dan mengontrol inputan dari user

Teori

Basic Form

Basic form adalah widget yang berfungsi sebagai inputan nilai seperti TextField, TextFormField, CheckBox, Switch, Dropdown, Radio, Dialog, DatePicker, BottomSheet, Snackbar dan lain-lain. Basic Form digunakan untuk validasi dan mengelola inputan dari berbagai field.

Form akan memberikan tampilan inputan kemudian inputan akan diperiksa apakah sudah sesuai dengan aturan atau format yang ditetapkan, selanjtunya data inputan akan diambil nilainya setelah proses pengecekan selesai dilakukan.

GlobalKey<FormState>

GlobalKey merupakan objek unik atau key yang digunakan untuk mengidentifikasi dan mengakses state secara global, artinya kita dapat mengakses widget dari widget mana saja.

FormState

  • FormState adalah kelas yang mengelola status dari Form, sepert status validasi setiap form inputan (TextFormField).
  • Menggunakan GlobalKey pada widget Form, maka kita dapat memanggil metode seperti validate() atau save() dari luar widget tersebut, biasanya dari onPressed pada ElevatedButton.

validate()

Metode validate() merupakan sebuah fungsi yang terdapat pada FormState yang digunakan untuk menjalankan validasi pada setiap TextFormField yang ada di dalam Form.

Ketika Anda memanggil _formKey.currentState!.validate(), maka Flutter akan:

  • Mengecek setiap TextFormField yang terikat pada Form tersebut.
  • Menjalankan fungsi validator yang telah didefinisikan pada setiap TextFormField.
  • Jika fungsi validator mengembalikan String (pesan error), validate() akan menghentikan proses dan mengembalikan nilai false. Pesan error tersebut akan ditampilkan di bawah TextFormField tersebut.
  • Jika semua fungsi validator mengembalikan null (tidak ada error), validate() akan mengembalikan nilai true.

setState()

setState merupakan sebuah method yang ada pada flutter, method ini digunakan untuk rebuild ulang state yang ada pada StatefulWidget atau secara sederhana method ini digunakan untuk memberitahu flutter bahwa state yang ada pada StatefulWidget telah berubah dan widget tersebut perlu untuk rebuild.

Const

Kata kunci const pada widget Flutter digunakan untuk membuat widget menjadi konstanta pada saat kompilasi (compile-time).

Penggunaan const dapat meningkatkan peforma karena widget const akan dibuat sekali dan akan disimpan pada memori sehingga dapat digunakan Kembali tanpa build ulang. Jika tidak menggunakan const maka widget akan di build() berkali-kali.

Dengan menggunakan kata kunci const maka flutter akan membangun / compile widget-widget tersebut sekali. Jika method build() dipanggil Kembali karena ada perubahan state pada widget lain maka widget yang sudah menggunakan kata kunci const tidak akan di rebuild sehingga proses reload lebih cepat dan menghemat sumber daya. Secara default flutter akan secara otomatis menjadikan widget menggunakan kata kunci const Ketika kode program disimpan.

TextField

Pengertian

TextField adalah widget yang digunakan untuk memasukkan text oleh pengguna, widget ini biasanya digunakan untuk membuat form inputan seperti form login, pencarian dll.

Fitur TextField

  1. Menerima input dari keyboard
  2. Memiliki property yang lengkap style, decoration, dan jenis inputan
  3. Dapat mengelola teks menggunakan TextEditingController

contoh penggunaan TextField

  1. Buat file dart baru dengan nama form-textfield didalam folder lib
  2. Buat tampilan basic form dengan menggunakan Widget TextField untuk inputan, ElevatedButton untuk memberikan event listener. Buat tampilan menggunakan kode program berikut :


import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Basic Form')),
        body: const MyForm(),
      ),
    );
  }
}

class MyForm extends StatefulWidget {
  const MyForm({super.key});

  @override
  State createState() => _MyFormState();
}

class _MyFormState extends State {
  TextEditingController _textEditingController = TextEditingController();
  String inputText = '';
  @override
  void dispose() {
    _textEditingController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(20.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const Text('Masukkan nama anda:'),
          const SizedBox(height: 10),
          TextField(
            decoration: InputDecoration(
              labelText: 'Nama lengkap',
              hintText: 'John Doe',
              border: OutlineInputBorder(),
              prefixIcon: Icon(Icons.person),
            ),
            controller: _textEditingController,
            keyboardType: TextInputType.text,
            onChanged: (text) {
              print('Sedang mengetik teks : ,$text');
            },
          ),
          const SizedBox(height: 20),
          ElevatedButton(
            onPressed: () {
              inputText = _textEditingController.text;
              ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(content: Text('Nama anda adalah, $inputText')),
              );
              setState(() {
                inputText = _textEditingController.text;
              });
            },
            style: ElevatedButton.styleFrom(
              backgroundColor: Colors.amber,
              foregroundColor: Colors.black,
            ),
            child: const Text('Tampilkan nama'),
          ),
          SizedBox(height: 20),
          Text('Nama Anda : $inputText', style: const TextStyle(fontSize: 20)),
        ],
      ),
    );
  }
}

            

Berikut hasil dari kode tersebut

text
text

TextFormField

Pengertian

TextFormField adalah widget versi lengkap dari TextField yang secara otomatis terintegrasi dengan logika validasi dan manajemen state dari sebuah form

Fitur TextFormField :

  1. Menerima input teks dari keyboard.
  2. Memiliki properti validator yang berfungsi untuk memeriksa apakah input sudah sesuai dengan aturan yang ditentukan.
  3. Menampilkan pesan error secara otomatis di bawah field jika validasi gagal
  4. Berinteraksi dengan FormState untuk melakukan validasi secara kolektif dengan validate() method.

contoh penggunaan properti pada widget Text

  1. Buat file baru didalam folder lib dengan nama form-textformfield
  2. Buat form inputan menggunakan 2 buah widget TextFormField dan 1 buah ElevatedButton, berikut kode program untuk tampilan tersebut.


import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text("Basic Form TextFormField")),
        body: const MyFormText(),
      ),
    );
  }
}

class MyFormText extends StatefulWidget {
  const MyFormText({super.key});

  @override
  State createState() => _MyFormTextState();
}

class _MyFormTextState extends State {
  final _formKey = GlobalKey<FormState> ();
  final _nameController = TextEditingController();
  final _emailController = TextEditingController();

  @override
  void dispose() {
    _nameController.dispose();
    _emailController.dispose();
    super.dispose();
  }

  void _submitForm() {
    if (_formKey.currentState!.validate()) {
      String name = _nameController.text;
      String email = _emailController.text;

      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Validasi $name, $email Berhasil')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Padding(
        padding: const EdgeInsets.all(20.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          children: [
            const SizedBox(height: 10),
            TextFormField(
              controller: _nameController,
              decoration: const InputDecoration(
                labelText: "Nama : ",
                border: OutlineInputBorder(),
              ),
              validator: (value) {
                if (value == null || value.isEmpty) {
                  return 'Masukkan nama anda';
                }
                return null;
              },
            ),
            const SizedBox(height: 10),
            TextFormField(
              controller: _emailController,
              decoration: const InputDecoration(
                labelText: "Email : ",
                border: OutlineInputBorder(),
              ),
              validator: (value) {
                if (value == null || value.isEmpty) {
                  return 'Masukkan email anda';
                }
                if (!value.contains('@')) {
                  return 'Email tidak valid';
                }
                return null;
              },
            ),
            const SizedBox(height: 10),
            SizedBox(
              width: double.infinity,
              child: ElevatedButton(
                onPressed: _submitForm,
                child: const Text('Submit'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
            

Berikut hasil dari kode tersebut

elevatedbutton
text

Tugas

Implementasi Basic Form Flutter

Buatlah form pendaftaran pengguna dengan menggunakan 4 buah form yaotu nama, email, password, confirm password dan satu tombol submit.

  • Buat 2 buah file dart yaitu main.dart dan user_registration_form.dart
  • Gunakan MaterialApp sebagai root pada main.dart
  • Set propreti home ke widget UserRegistrationForm
  • Gunakan statefulwidget pada UserRegistrationForm
  • Gunakan widget TextFormField untuk inputan, gunakan method validador untuk validasi
  • Validasi : inputan email harus menggunakan format @, inputan password dan confirm password harus sama atau valid
  • Gunakan method onPressed dan Ketika di pencet akan memanggil _submitForm

main.dart

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

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "User Registration Form",
      debugShowCheckedModeBanner: false,
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const UserRegistrationForm(),
    );
  }
}
              

user_registration_form.dart


import 'package:flutter/material.dart';

class UserRegistrationForm extends StatefulWidget {
  const UserRegistrationForm({super.key});
  @override
  State createState() => _userRegistrationFormState();
}

class _userRegistrationFormState extends State {
  final TextEditingController _nameController = TextEditingController();
  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();
  final TextEditingController _confirmPasswordController =
      TextEditingController();
  final GlobalKey _formKey = GlobalKey();

  bool _obscurePassword = true;
  bool _obscureConfirmPassword = true;

  @override
  void dispose() {
    _nameController.dispose();
    _emailController.dispose();
    _passwordController.dispose();
    _confirmPasswordController.dispose();
    super.dispose();
  }

  void _submitForm() {
    if (_formKey.currentState!.validate()) {
      final name = _nameController.text.trim();
      final email = _emailController.text.trim();

      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text('Pendaftaran berhasil. Nama: $name, Email: $email'),
        ),
      );
    } else {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text('Validasi gagal. Silakan periksa kembali input Anda.'),
        ),
      );
    }
  }

  String? _validateName(String? value) {
    if (value == null || value.trim().isEmpty) {
      return 'Nama tidak boleh kosong';
    }
    return null;
  }

  String? _validateEmail(String? value) {
    if (value == null || value.trim().isEmpty) {
      return 'Email tidak boleh kosong';
    }
    if (!value.contains('@')) {
      return 'Format email tidak valid, harus mengandung "@"';
    }
    return null;
  }

  String? _validatePassword(String? value) {
    if (value == null || value.trim().isEmpty) {
      return 'Password tidak boleh kosong';
    }
    if (value.trim().length < 6) {
      return 'Password harus terdiri dari minimal 6 karakter';
    }
    return null;
  }

  String? _validateConfirmPassword(String? value) {
    if (value == null || value.trim().isEmpty) {
      return 'Konfirmasi password tidak boleh kosong';
    }
    if (value != _passwordController.text.trim()) {
      return 'Password dan konfirmasi password tidak cocok';
    }
    return null;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Form Pendaftaran Pengguna")),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16.0),
        child: Form(
          key: _formKey,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              TextFormField(
                controller: _nameController,
                decoration: const InputDecoration(
                  labelText: 'Nama',
                  border: OutlineInputBorder(),
                ),
                validator: _validateName,
                textInputAction: TextInputAction.next,
              ),
              const SizedBox(height: 12),

              TextFormField(
                controller: _emailController,
                decoration: const InputDecoration(
                  labelText: 'Email',
                  border: OutlineInputBorder(),
                ),
                keyboardType: TextInputType.emailAddress,
                validator: _validateEmail,
                textInputAction: TextInputAction.next,
              ),
              const SizedBox(height: 12),

              TextFormField(
                controller: _passwordController,
                decoration: InputDecoration(
                  labelText: 'Password',
                  border: const OutlineInputBorder(),
                  suffixIcon: IconButton(
                    icon: Icon(
                      _obscurePassword
                          ? Icons.visibility
                          : Icons.visibility_off,
                    ),
                    onPressed: () {
                      setState(() {
                        _obscurePassword = !_obscurePassword;
                      });
                    },
                  ),
                ),
                obscureText: _obscurePassword,
                validator: _validatePassword,
                textInputAction: TextInputAction.next,
              ),
              const SizedBox(height: 12),

              TextFormField(
                controller: _confirmPasswordController,
                decoration: InputDecoration(
                  labelText: 'Konfirmasi Password',
                  border: const OutlineInputBorder(),
                  suffixIcon: IconButton(
                    icon: Icon(
                      _obscureConfirmPassword
                          ? Icons.visibility
                          : Icons.visibility_off,
                    ),
                    onPressed: () {
                      setState(() {
                        _obscureConfirmPassword = !_obscureConfirmPassword;
                      });
                    },
                  ),
                ),
                obscureText: _obscureConfirmPassword,
                validator: _validateConfirmPassword,
                textInputAction: TextInputAction.done,
                onFieldSubmitted: (_) => _submitForm(),
              ),
              const SizedBox(height: 20),

              ElevatedButton(
                onPressed: _submitForm,
                child: const Padding(
                  padding: EdgeInsets.symmetric(vertical: 14.0),
                  child: Text('Daftar', style: TextStyle(fontSize: 16)),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
              

Berikut tampilan hasilnya.

Link Project

GitHub Repository