Widgets

Modal

Pengantar

Nylo Website menyediakan sistem modal yang dibangun di atas bottom sheet modal.

Kelas BottomSheetModal memberi Anda API yang fleksibel untuk menampilkan overlay konten dengan aksi, header, tombol tutup, dan styling kustom.

Modal berguna untuk:

  • Dialog konfirmasi (misalnya, logout, hapus)
  • Form input cepat
  • Action sheet dengan beberapa opsi
  • Overlay informasi

Membuat Modal

Anda dapat membuat modal baru menggunakan Metro CLI:

metro make:bottom_sheet_modal payment_options

Ini menghasilkan dua hal:

  1. Widget konten modal di lib/resources/widgets/bottom_sheet_modals/modals/payment_options_modal.dart:
import 'package:flutter/material.dart';

/// Payment Options Modal
///
/// Used in BottomSheetModal.showPaymentOptions()
class PaymentOptionsModal extends StatelessWidget {
  const PaymentOptionsModal({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('PaymentOptionsModal').headingLarge(),
      ],
    );
  }
}
  1. Metode statis yang ditambahkan ke kelas BottomSheetModal Anda di lib/resources/widgets/bottom_sheet_modals/bottom_sheet_modals.dart:
/// Show Payment Options modal
static Future<void> showPaymentOptions(BuildContext context) {
  return displayModal(
    context,
    isScrollControlled: false,
    child: const PaymentOptionsModal(),
  );
}

Anda kemudian dapat menampilkan modal dari mana saja:

BottomSheetModal.showPaymentOptions(context);

Jika modal dengan nama yang sama sudah ada, gunakan flag --force untuk menimpanya:

metro make:bottom_sheet_modal payment_options --force

Penggunaan Dasar

Tampilkan modal menggunakan BottomSheetModal:

BottomSheetModal.showLogout(context);

Membuat Modal

Pola yang direkomendasikan adalah membuat kelas BottomSheetModal dengan metode statis untuk setiap tipe modal. Boilerplate menyediakan struktur ini:

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

class BottomSheetModal extends NyBaseModal {
  static ModalShowFunction get displayModal => displayModal;

  /// Show Logout modal
  static Future<void> showLogout(
    BuildContext context, {
    Function()? onLogoutPressed,
    Function()? onCancelPressed,
  }) {
    return displayModal(
      context,
      isScrollControlled: false,
      child: const LogoutModal(),
      actionsRow: [
        Button.secondary(
          text: "Logout",
          onPressed: onLogoutPressed ?? () => routeToInitial(),
        ),
        Button(
          text: "Cancel",
          onPressed: onCancelPressed ?? () => Navigator.pop(context),
        ),
      ],
    );
  }
}

Panggil dari mana saja:

BottomSheetModal.showLogout(context);

// With custom callbacks
BottomSheetModal.showLogout(
  context,
  onLogoutPressed: () {
    // Custom logout logic
  },
  onCancelPressed: () {
    Navigator.pop(context);
  },
);

BottomSheetModal

displayModal<T>() adalah metode inti untuk menampilkan modal.

Parameter

Parameter Tipe Default Deskripsi
context BuildContext required Build context untuk modal
child Widget required Widget konten utama
actionsRow List<Widget> [] Widget aksi ditampilkan dalam baris horizontal
actionsColumn List<Widget> [] Widget aksi ditampilkan secara vertikal
height double? null Tinggi tetap untuk modal
header Widget? null Widget header di bagian atas
useSafeArea bool true Membungkus konten dalam SafeArea
isScrollControlled bool false Mengizinkan modal dapat di-scroll
showCloseButton bool false Menampilkan tombol tutup X
headerPadding EdgeInsets? null Padding saat header ada
backgroundColor Color? null Warna latar belakang modal
showHandle bool true Menampilkan handle tarik di bagian atas
closeButtonColor Color? null Warna latar belakang tombol tutup
closeButtonIconColor Color? null Warna ikon tombol tutup
modalDecoration BoxDecoration? null Dekorasi kustom untuk container modal
handleColor Color? null Warna handle tarik

Aksi

Aksi adalah tombol yang ditampilkan di bagian bawah modal.

Aksi baris ditempatkan berdampingan, masing-masing mengambil ruang yang sama:

displayModal(
  context,
  child: Text("Are you sure?"),
  actionsRow: [
    ElevatedButton(
      onPressed: () => Navigator.pop(context, true),
      child: Text("Yes"),
    ),
    TextButton(
      onPressed: () => Navigator.pop(context, false),
      child: Text("No"),
    ),
  ],
);

Aksi kolom ditumpuk secara vertikal:

displayModal(
  context,
  child: Text("Choose an option"),
  actionsColumn: [
    ElevatedButton(
      onPressed: () => Navigator.pop(context, true),
      child: Text("Yes"),
    ),
    TextButton(
      onPressed: () => Navigator.pop(context, false),
      child: Text("No"),
    ),
  ],
);

Header

Tambahkan header yang berada di atas konten utama:

displayModal(
  context,
  header: Container(
    padding: EdgeInsets.all(16),
    color: Colors.blue,
    child: Text(
      "Modal Title",
      style: TextStyle(color: Colors.white, fontSize: 18),
    ),
  ),
  child: Text("Modal content goes here"),
);

Tombol Tutup

Tampilkan tombol tutup di pojok kanan atas:

displayModal(
  context,
  showCloseButton: true,
  closeButtonColor: Colors.grey.shade200,
  closeButtonIconColor: Colors.black,
  child: Padding(
    padding: EdgeInsets.all(24),
    child: Text("Content with close button"),
  ),
);

Dekorasi Kustom

Kustomisasi tampilan container modal:

displayModal(
  context,
  backgroundColor: Colors.transparent,
  modalDecoration: BoxDecoration(
    color: Colors.white,
    borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
    boxShadow: [
      BoxShadow(color: Colors.black26, blurRadius: 10),
    ],
  ),
  handleColor: Colors.grey.shade400,
  child: Text("Custom styled modal"),
);

BottomModalSheetStyle

BottomModalSheetStyle mengkonfigurasi tampilan bottom sheet modal yang digunakan oleh form picker dan komponen lainnya:

BottomModalSheetStyle(
  backgroundColor: NyColor(light: Colors.white, dark: Colors.grey.shade900),
  barrierColor: NyColor(light: Colors.black54, dark: Colors.black87),
  useRootNavigator: false,
  titleStyle: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
  itemStyle: TextStyle(fontSize: 16),
  clearButtonStyle: TextStyle(color: Colors.red),
)
Properti Tipe Deskripsi
backgroundColor NyColor? Warna latar belakang modal
barrierColor NyColor? Warna overlay di belakang modal
useRootNavigator bool Menggunakan root navigator (default: false)
routeSettings RouteSettings? Pengaturan rute untuk modal
titleStyle TextStyle? Style untuk teks judul
itemStyle TextStyle? Style untuk teks item daftar
clearButtonStyle TextStyle? Style untuk teks tombol hapus

Contoh

Modal Konfirmasi

static Future<bool?> showConfirm(
  BuildContext context, {
  required String message,
}) {
  return displayModal<bool>(
    context,
    child: Padding(
      padding: EdgeInsets.symmetric(vertical: 16),
      child: Text(message, textAlign: TextAlign.center),
    ),
    actionsRow: [
      ElevatedButton(
        onPressed: () => Navigator.pop(context, true),
        child: Text("Confirm"),
      ),
      TextButton(
        onPressed: () => Navigator.pop(context, false),
        child: Text("Cancel"),
      ),
    ],
  );
}

// Usage
bool? confirmed = await BottomSheetModal.showConfirm(
  context,
  message: "Delete this item?",
);
if (confirmed == true) {
  // delete the item
}

Modal Konten yang Dapat Di-scroll

displayModal(
  context,
  isScrollControlled: true,
  height: MediaQuery.of(context).size.height * 0.8,
  showCloseButton: true,
  header: Padding(
    padding: EdgeInsets.all(16),
    child: Text("Terms of Service", style: TextStyle(fontSize: 20)),
  ),
  child: SingleChildScrollView(
    child: Text(longTermsText),
  ),
);

Action Sheet

displayModal(
  context,
  showHandle: true,
  child: Text("Share via", style: TextStyle(fontSize: 18)),
  actionsColumn: [
    ListTile(
      leading: Icon(Icons.email),
      title: Text("Email"),
      onTap: () {
        Navigator.pop(context);
        shareViaEmail();
      },
    ),
    ListTile(
      leading: Icon(Icons.message),
      title: Text("Message"),
      onTap: () {
        Navigator.pop(context);
        shareViaMessage();
      },
    ),
    ListTile(
      leading: Icon(Icons.copy),
      title: Text("Copy Link"),
      onTap: () {
        Navigator.pop(context);
        copyLink();
      },
    ),
  ],
);