Basics

NyState

Pengantar

NyState adalah versi yang diperluas dari kelas State standar Flutter. Kelas ini menyediakan fungsionalitas tambahan untuk membantu mengelola state halaman dan widget Anda dengan cara yang lebih efisien.

Anda dapat berinteraksi dengan state persis seperti yang Anda lakukan dengan state Flutter biasa, tetapi dengan manfaat tambahan dari NyState.

Mari kita bahas cara menggunakan NyState.

Cara Menggunakan NyState

Anda dapat mulai menggunakan kelas ini dengan meng-extend-nya.

Contoh

class _HomePageState extends NyState<HomePage> {

  @override
  get init => () async {

  };

  @override
  view(BuildContext context) {
    return Scaffold(
        body: Text("The page loaded")
    );
  }

Method init digunakan untuk menginisialisasi state halaman. Anda dapat menggunakan method ini sebagai async atau tanpa async dan di balik layar, ia akan menangani panggilan async dan menampilkan loader.

Method view digunakan untuk menampilkan UI halaman.

Membuat stateful widget baru dengan NyState

Untuk membuat stateful widget baru di Nylo Website, Anda dapat menjalankan perintah di bawah ini.

metro make:stateful_widget ProfileImage

Gaya Loading

Anda dapat menggunakan properti loadingStyle untuk mengatur gaya loading halaman Anda.

Contoh

class _ProfileImageState extends NyState<ProfileImage> {

  @override
  LoadingStyleType get loadingStyle => LoadingStyleType.normal();

  @override
  get init => () async {
    await sleep(3); // simulate a network call for 3 seconds
  };

loadingStyle default akan menjadi Widget loading Anda (resources/widgets/loader_widget.dart). Anda dapat menyesuaikan loadingStyle untuk memperbarui gaya loading.

Berikut tabel untuk berbagai gaya loading yang dapat Anda gunakan: // normal, skeletonizer, none

Gaya Deskripsi
normal Gaya loading default
skeletonizer Gaya loading skeleton
none Tanpa gaya loading

Anda dapat mengubah gaya loading seperti ini:

@override
LoadingStyle get loadingStyle => LoadingStyle.normal();
// or
@override
LoadingStyle get loadingStyle => LoadingStyle.skeletonizer();

Jika Anda ingin memperbarui Widget loading di salah satu gaya, Anda dapat memasukkan child ke LoadingStyle.

@override
LoadingStyle get loadingStyle => LoadingStyle.normal(
    child: Center(
        child: Text("Loading..."),
    ),
);
// same for skeletonizer
@override
LoadingStyle get loadingStyle => LoadingStyle.skeletonizer(
    child: Container(
        child: PageLayoutForSkeletonizer(),
    )
);

Sekarang, saat tab sedang loading, teks "Loading..." akan ditampilkan.

Contoh di bawah ini:

class _HomePageState extends NyState<HomePage> {
    get init => () async {
        await sleep(3); // simulate a network call for 3 seconds
    };

    @override
    LoadingStyle get loadingStyle => LoadingStyle.normal(
        child: Center(
            child: Text("Loading..."),
        ),
    );

    @override
    Widget view(BuildContext context) {
        return Scaffold(
            body: Text("The page loaded")
        );
    }
    ...
}

Aksi State

Di Nylo, Anda dapat mendefinisikan aksi kecil di Widget Anda yang dapat dipanggil dari kelas lain. Ini berguna jika Anda ingin memperbarui state widget dari kelas lain.

Pertama, Anda harus mendefinisikan aksi Anda di widget. Ini bekerja untuk NyState dan NyPage.

class _MyWidgetState extends NyState<MyWidget> {

  @override
  get init => () async {
    // handle how you want to initialize the state
  };

  @override
  get stateActions => {
    "hello_world_in_widget": () {
      print('Hello world');
    },
    "update_user_name": (User user) async {
      // Example with data
      _userName = user.name;
      setState(() {});
    },
    "show_toast": (String message) async {
      showToastSuccess(description: message);
    },
  };
}

Kemudian, Anda dapat memanggil aksi dari kelas lain menggunakan method stateAction.

stateAction('hello_world_in_widget', state: MyWidget.state);

// Another example with data
User user = User(name: "John Doe");
stateAction('update_user_name', state: MyWidget.state, data: user);
// Another example with data
stateAction('show_toast', state: MyWidget.state, data: "Hello world");

Jika Anda menggunakan stateActions dengan NyPage, Anda harus menggunakan path dari halaman tersebut.

stateAction('hello_world_in_widget', state: ProfilePage.path);

// Another example with data
User user = User(name: "John Doe");
stateAction('update_user_name', state: ProfilePage.path, data: user);

// Another example with data
stateAction('show_toast', state: ProfilePage.path, data: "Hello world");

Ada juga kelas lain bernama StateAction, kelas ini memiliki beberapa method yang dapat Anda gunakan untuk memperbarui state widget Anda.

  • refreshPage - Segarkan halaman.
  • pop - Pop halaman.
  • showToastSorry - Tampilkan notifikasi toast sorry.
  • showToastWarning - Tampilkan notifikasi toast warning.
  • showToastInfo - Tampilkan notifikasi toast info.
  • showToastDanger - Tampilkan notifikasi toast danger.
  • showToastOops - Tampilkan notifikasi toast oops.
  • showToastSuccess - Tampilkan notifikasi toast success.
  • showToastCustom - Tampilkan notifikasi toast kustom.
  • validate - Validasi data dari widget Anda.
  • changeLanguage - Perbarui bahasa di aplikasi.
  • confirmAction - Lakukan aksi konfirmasi.

Contoh

class _UpgradeButtonState extends NyState<UpgradeButton> {

  view(BuildContext context) {
    return Button.primary(
      onPressed: () {
        StateAction.showToastSuccess(UpgradePage.state,
          description: "You have successfully upgraded your account",
        );
      },
      text: "Upgrade",
    );
  }
}

Anda dapat menggunakan kelas StateAction untuk memperbarui state halaman/widget mana pun di aplikasi Anda selama widget tersebut dikelola state-nya.

Helper

lockRelease
showToast isLoading
validate afterLoad
afterNotLocked afterNotNull
whenEnv setLoading
pop isLocked
changeLanguage confirmAction
showToastSuccess showToastOops
showToastDanger showToastInfo
showToastWarning showToastSorry

Reboot

Method ini akan menjalankan ulang method init di state Anda. Berguna jika Anda ingin menyegarkan data di halaman.

Contoh

class _HomePageState extends NyState<HomePage> {

  List<User> users = [];

  @override
  get init => () async {
    users = await api<ApiService>((request) => request.fetchUsers());
  };

  @override
  Widget view(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Users"),
          actions: [
            IconButton(
              icon: Icon(Icons.refresh),
              onPressed: () {
                reboot(); // refresh the data
              },
            )
          ],
        ),
        body: ListView.builder(
          itemCount: users.length,
          itemBuilder: (context, index) {
            return Text(users[index].firstName);
          }
        ),
    );
  }
}

Pop

pop - Hapus halaman saat ini dari stack.

Contoh

class _HomePageState extends NyState<HomePage> {

  popView() {
    pop();
  }

  @override
  Widget view(BuildContext context) {
    return Scaffold(
      body: InkWell(
        onTap: popView,
        child: Text("Pop current view")
      )
    );
  }

showToast

Tampilkan notifikasi toast pada context.

Contoh

class _HomePageState extends NyState<HomePage> {

  displayToast() {
    showToast(
        title: "Hello",
        description: "World",
        icon: Icons.account_circle,
        duration: Duration(seconds: 2),
        style: ToastNotificationStyleType.INFO // SUCCESS, INFO, DANGER, WARNING
    );
  }

  @override
  Widget view(BuildContext context) {
    return Scaffold(
      body: InkWell(
        onTap: displayToast,
        child: Text("Display a toast")
      )
    );
  }

validate

Helper validate melakukan pemeriksaan validasi pada data.

Anda dapat mempelajari lebih lanjut tentang validator di sini.

Contoh

class _HomePageState extends NyState<HomePage> {
TextEditingController _textFieldControllerEmail = TextEditingController();

  handleForm() {
    String textEmail = _textFieldControllerEmail.text;

    validate(rules: {
        "email address": [textEmail, "email"]
      }, onSuccess: () {
      print('passed validation')
    });
  }

changeLanguage

Anda dapat memanggil changeLanguage untuk mengubah file json /lang yang digunakan di perangkat.

Pelajari lebih lanjut tentang lokalisasi di sini.

Contoh

class _HomePageState extends NyState<HomePage> {

  changeLanguageES() {
    await changeLanguage('es');
  }

  @override
  Widget view(BuildContext context) {
    return Scaffold(
      body: InkWell(
        onTap: changeLanguageES,
        child: Text("Change Language".tr())
      )
    );
  }

whenEnv

Anda dapat menggunakan whenEnv untuk menjalankan fungsi ketika aplikasi Anda berada dalam keadaan tertentu. Contoh: variabel APP_ENV di dalam file .env Anda diatur ke 'developing', APP_ENV=developing.

Contoh

class _HomePageState extends NyState<HomePage> {

  TextEditingController _textEditingController = TextEditingController();

  @override
  get init => () {
    whenEnv('developing', perform: () {
      _textEditingController.text = 'test-email@gmail.com';
    });
  };

lockRelease

Method ini akan mengunci state setelah fungsi dipanggil, hanya setelah method selesai barulah ia mengizinkan pengguna membuat permintaan berikutnya. Method ini juga akan memperbarui state, gunakan isLocked untuk memeriksa.

Contoh terbaik untuk menunjukkan lockRelease adalah membayangkan kita memiliki layar login ketika pengguna mengetuk 'Login'. Kita ingin melakukan panggilan async untuk login pengguna tetapi kita tidak ingin method dipanggil berkali-kali karena dapat membuat pengalaman yang tidak diinginkan.

Berikut contoh di bawah ini.

class _LoginPageState extends NyState<LoginPage> {

  _login() async {
    await lockRelease('login_to_app', perform: () async {

      await Future.delayed(Duration(seconds: 4), () {
        print('Pretend to login...');
      });

    });
  }

  @override
  Widget view(BuildContext context) {
    return Scaffold(
        body: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (isLocked('login_to_app'))
              AppLoader(),
            Center(
              child: InkWell(
                onTap: _login,
                child: Text("Login"),
              ),
            )
          ],
        )
    );
  }

Setelah Anda mengetuk method _login, ia akan memblokir permintaan berikutnya sampai permintaan asli selesai. Helper isLocked('login_to_app') digunakan untuk memeriksa apakah tombol terkunci. Pada contoh di atas, Anda dapat melihat kami menggunakannya untuk menentukan kapan menampilkan Widget loading.

isLocked

Method ini akan memeriksa apakah state terkunci menggunakan helper lockRelease.

Contoh

class _HomePageState extends NyState<HomePage> {

  @override
  Widget view(BuildContext context) {
    return Scaffold(
        body: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (isLocked('login_to_app'))
              AppLoader(),
          ],
        )
    );
  }

view

Method view digunakan untuk menampilkan UI halaman.

Contoh

class _HomePageState extends NyState<HomePage> {

  @override
  Widget view(BuildContext context) {
      return Scaffold(
          body: Center(
              child: Text("My Page")
          )
      );
  }
}

confirmAction

Method confirmAction akan menampilkan dialog kepada pengguna untuk mengonfirmasi aksi. Method ini berguna jika Anda ingin pengguna mengonfirmasi aksi sebelum melanjutkan.

Contoh

_logout() {
 confirmAction(() {
    // logout();
 }, title: "Logout of the app?");
}

showToastSuccess

Method showToastSuccess akan menampilkan notifikasi toast sukses kepada pengguna.

Contoh

_login() {
    ...
    showToastSuccess(
        description: "You have successfully logged in"
    );
}

showToastOops

Method showToastOops akan menampilkan notifikasi toast oops kepada pengguna.

Contoh

_error() {
    ...
    showToastOops(
        description: "Something went wrong"
    );
}

showToastDanger

Method showToastDanger akan menampilkan notifikasi toast danger kepada pengguna.

Contoh

_error() {
    ...
    showToastDanger(
        description: "Something went wrong"
    );
}

showToastInfo

Method showToastInfo akan menampilkan notifikasi toast info kepada pengguna.

Contoh

_info() {
    ...
    showToastInfo(
        description: "Your account has been updated"
    );
}

showToastWarning

Method showToastWarning akan menampilkan notifikasi toast warning kepada pengguna.

Contoh

_warning() {
    ...
    showToastWarning(
        description: "Your account is about to expire"
    );
}

showToastSorry

Method showToastSorry akan menampilkan notifikasi toast sorry kepada pengguna.

Contoh

_sorry() {
    ...
    showToastSorry(
        description: "Your account has been suspended"
    );
}

isLoading

Method isLoading akan memeriksa apakah state sedang loading.

Contoh

class _HomePageState extends NyState<HomePage> {

  @override
  Widget build(BuildContext context) {
    if (isLoading()) {
      return AppLoader();
    }

    return Scaffold(
        body: Text("The page loaded", style: TextStyle(
          color: colors().primaryContent
        )
      )
    );
  }

afterLoad

Method afterLoad dapat digunakan untuk menampilkan loader sampai state selesai 'loading'.

Anda juga dapat memeriksa key loading lain menggunakan parameter loadingKey afterLoad(child: () {}, loadingKey: 'home_data').

Contoh

class _HomePageState extends NyState<HomePage> {

  @override
  get init => () {
    awaitData(perform: () async {
        await sleep(4);
        print('4 seconds after...');
    });
  };

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: afterLoad(child: () {
          return Text("Loaded");
        })
    );
  }

afterNotLocked

Method afterNotLocked akan memeriksa apakah state terkunci.

Jika state terkunci, ia akan menampilkan widget [loading].

Contoh

class _HomePageState extends NyState<HomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Container(
          alignment: Alignment.center,
          child: afterNotLocked('login', child: () {
            return MaterialButton(
              onPressed: () {
                login();
              },
              child: Text("Login"),
            );
          }),
        )
    );
  }

  login() async {
    await lockRelease('login', perform: () async {
      await sleep(4);
      print('4 seconds after...');
    });
  }
}

afterNotNull

Anda dapat menggunakan afterNotNull untuk menampilkan widget loading sampai variabel telah diatur.

Bayangkan Anda perlu mengambil akun pengguna dari DB menggunakan panggilan Future yang mungkin memakan waktu 1-2 detik, Anda dapat menggunakan afterNotNull pada nilai tersebut sampai Anda memiliki datanya.

Contoh

class _HomePageState extends NyState<HomePage> {

  User? _user;

  @override
  get init => () async {
    _user = await api<ApiService>((request) => request.fetchUser()); // example
    setState(() {});
  };

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: afterNotNull(_user, child: () {
          return Text(_user!.firstName);
        })
    );
  }

setLoading

Anda dapat beralih ke state 'loading' dengan menggunakan setLoading.

Parameter pertama menerima bool apakah sedang loading atau tidak, parameter berikutnya memungkinkan Anda mengatur nama untuk state loading, contoh: setLoading(true, name: 'refreshing_content');.

Contoh

class _HomePageState extends NyState<HomePage> {

  @override
  get init => () async {
    setLoading(true, name: 'refreshing_content');

    await sleep(4);

    setLoading(false, name: 'refreshing_content');
  };

  @override
  Widget build(BuildContext context) {
    if (isLoading(name: 'refreshing_content')) {
      return AppLoader();
    }

    return Scaffold(
        body: Text("The page loaded")
    );
  }