Laporan Praktikum 3

Pendahuluan

Pada praktikum ini dipelajari penggunaan basic form di Flutter. Pertama ada TextField, yaitu widget untuk menerima input teks dari pengguna, misalnya nama atau kata kunci, dan biasanya dipadukan dengan TextEditingController agar input bisa dikelola. Lalu ada TextFormField, yaitu versi lebih lengkap dari TextField yang sudah terintegrasi dengan logika validasi sehingga bisa langsung memeriksa apakah data yang dimasukkan sesuai aturan. Setelah itu digunakan GlobalKey untuk mengontrol status form, misalnya menjalankan validasi semua field sekaligus dengan method validate(). Kemudian ada juga fungsi setState() yang dipakai untuk memperbarui tampilan ketika terjadi perubahan data. Dengan menggabungkan komponen-komponen tersebut, mahasiswa dapat membuat form sederhana yang tidak hanya menerima input, tetapi juga melakukan validasi dan menampilkan hasil inputan pengguna.

Basic Form TextField

Basic Form merupakan 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.

Untuk Membuat Form TextField, . Buat file dart baru dengan nama form-textfield didalam folder lib, lalu 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 {
  
  final TextEditingController _teksEditingController = TextEditingController();

  String inputText = '';

  @override
  void dispose() {
    _teksEditingController.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 :',
            style: TextStyle(fontSize: 16),
          ),
          const SizedBox(height: 10),
          
          TextField(
            controller: _teksEditingController,
            keyboardType: TextInputType.text,
            onChanged: (text) {
              print('Sedang mengetik teks : $text');
            },
            decoration: const InputDecoration(
              labelText: 'Nama Lengkap',
              hintText: 'Misalnya Fikhri',
              border: OutlineInputBorder(),
              prefixIcon: Icon(Icons.person),
            ),
          ),
          const SizedBox(height: 20),
          ElevatedButton(
            onPressed: () {
              String inputText = _teksEditingController.text;
              ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(content: Text('Nama anda adalah $inputText!')),
              );
              setState(() {
                inputText = _teksEditingController.text;
              });
            },
            style: ElevatedButton.styleFrom(
              backgroundColor: Colors.amber,
              foregroundColor: Colors.black,
            ),
            child: const Text('Tampilkan Nama'),
          ),
          const SizedBox(height: 20),

            Text(
                'Nama Anda: ${_teksEditingController.text}',
                style: const TextStyle(fontSize: 20)
            ),
        ],
      ),
    );
  }
}

Dan pada code diatas, juga ditambahkan Method setState untuk menampilkan hasil inputan. sehingga hasilnya seperti gambar dibawah ini:

Keterangan code :

Line 14

final TextEditingController _teksEditingController = TextEditingController();

→ Membuat sebuah controller yang berfungsi untuk mengambil inputan dari user.

Line 20–23

dispose()

→ Method yang digunakan untuk membersihkan controller setelah widget tidak digunakan lagi.

Line 34–42 (TextField)

controller: _teksEditingController

→ menghubungkan TextField dengan controller.

keyboardType: TextInputType.text

→ menentukan jenis inputan yang boleh dimasukkan (teks).

onChanged: (text)

→ menambahkan event listener saat user sedang mengetik.

Line 45–56 (ElevatedButton)

Pada

onPressed
dibuat variabel
inputText
yang menampung nilai dari controller.

Inputan tersebut ditampilkan menggunakan SnackBar.

Digunakan

setState()
agar tampilan teks di layar ikut diperbarui sesuai input terbaru.

Line 62–65 (Text)

→ Menampilkan kembali inputan user ke layar.

TextFormField

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

Untuk membuatnya, buat file baru dengan nama form-textformfield.dart dan tambahkan code program berikut ini :

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();
  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'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Dan hasilnya akan seperi ini :

Keterangan code :

Line 28

final _formKey = GlobalKey();

→ Membuat sebuah GlobalKey untuk mengontrol status form dan memanggil method seperti validate().

Line 29–30

final _nameController = TextEditingController();
final _emailController = TextEditingController();

→ Controller untuk mengambil inputan dari TextFormField (nama dan email).

Line 32–36 (dispose)

→ Membersihkan controller ketika widget sudah tidak digunakan lagi agar tidak terjadi kebocoran memori

Line 38–46 (_submitForm)

if (_formKey.currentState!.validate())();

→ menjalankan validasi pada semua field. Jika valid, nilai dari name dan email diambil melalui controller. Inputan ditampilkan menggunakan SnackBar.

Line 52–99 (build → Form)

TextFormField Nama

TextFormField Email

ElevatedButton

Tugas Kabataku

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('Kalkulator Kabataku'),
        ),
        body: const MyForm(),
      ),
    );
  }
}

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

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

class _MyFormState extends State {
  final TextEditingController _angka1Controller = TextEditingController();
  final TextEditingController _angka2Controller = TextEditingController();

  String hasil = '';

  @override
  void dispose() {
    _angka1Controller.dispose();
    _angka2Controller.dispose();
    super.dispose();
  }

  void _hitung(String operasi) {
    double angka1 = double.tryParse(_angka1Controller.text) ?? 0;
    double angka2 = double.tryParse(_angka2Controller.text) ?? 0;
    double temp = 0;

    switch (operasi) {
      case '+':
        temp = angka1 + angka2;
        break;
      case '-':
        temp = angka1 - angka2;
        break;
      case '×':
        temp = angka1 * angka2;
        break;
      case '÷':
        temp = angka2 != 0 ? angka1 / angka2 : double.nan;
        break;
    }

    setState(() {
      hasil = temp.isNaN ? 'Tidak bisa dibagi 0' : temp.toStringAsFixed(2);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(20.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const Text(
            'Masukkan 2 angka untuk dihitung:',
            style: TextStyle(fontSize: 16),
          ),
          const SizedBox(height: 10),

          TextField(
            controller: _angka1Controller,
            keyboardType: TextInputType.number,
            decoration: const InputDecoration(
              labelText: 'Angka Pertama',
              border: OutlineInputBorder(),
            ),
          ),
          const SizedBox(height: 10),

          TextField(
            controller: _angka2Controller,
            keyboardType: TextInputType.number,
            decoration: const InputDecoration(
              labelText: 'Angka Kedua',
              border: OutlineInputBorder(),
            ),
          ),
          const SizedBox(height: 20),

          Row(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: [
    ElevatedButton(
      onPressed: () => _hitung('+'),
      child: const Text('Tambah (+)'),
    ),
    ElevatedButton(
      onPressed: () => _hitung('-'),
      child: const Text('Kurang (-)'),
    ),
    ElevatedButton(
      onPressed: () => _hitung('×'),
      child: const Text('Kali (×)'),
    ),
    ElevatedButton(
      onPressed: () => _hitung('÷'),
      child: const Text('Bagi (÷)'),
    ),
  ],
),

          const SizedBox(height: 20),

          Text(
            'Hasil: $hasil',
            style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
          ),
        ],
      ),
    );
  }
}

Maka outputnya :

Jadi, Aplikasi dimulai dari main() yang menjalankan Kabataku, lalu menampilkan MaterialApp dengan Scaffold berisi AppBar dan body MyCalc. Widget MyCalc dibuat StatefulWidget karena harus menyimpan input angka dan hasil perhitungan. Di dalam state _MyCalcState, terdapat dua TextEditingController untuk menangkap input angka pertama dan angka kedua, serta variabel result untuk menyimpan hasil operasi. Fungsi _calculate() menerima operator (+, −, ×, ÷), mengubah input teks menjadi angka dengan double.tryParse, lalu menghitung sesuai operator dan memperbarui state. Pada tampilan, terdapat dua TextField masing-masing untuk angka pertama dan angka kedua dengan dekorasi label, hint, dan border. Setelah itu, disediakan empat tombol ElevatedButton untuk operasi tambah, kurang, kali, dan bagi, yang masing-masing memanggil fungsi _calculate(). Terakhir, hasil perhitungan ditampilkan dalam sebuah Text di bawah tombol. Dengan struktur ini, kode mencakup alur lengkap mulai dari input, pemilihan operasi, hingga menampilkan hasil.

Kesimpulan

Pada praktikum ini dipelajari cara membuat form dasar di Flutter menggunakan TextField dan TextFormField. Dengan TextField, input pengguna dapat ditangkap melalui TextEditingController dan ditampilkan kembali menggunakan SnackBar maupun widget Text. Sedangkan TextFormField memberikan fitur tambahan berupa validasi otomatis yang dapat dicek dengan GlobalKey.

Selain itu, pada tugas kalkulator Kabataku, dipelajari penerapan input ganda melalui dua buah TextField, pengolahan data menggunakan fungsi perhitungan sederhana (tambah, kurang, kali, bagi), serta penggunaan setState() untuk memperbarui tampilan hasil di layar.

Secara keseluruhan, praktikum ini memberikan pemahaman mengenai bagaimana Flutter mengelola inputan pengguna, melakukan validasi, hingga menampilkan hasil, yang menjadi dasar penting dalam pengembangan aplikasi mobile interaktif.