کاهش کد Boilerplate در رابط کاربری فلاتر — به زبان ساده

۵۱ بازدید
آخرین به‌روزرسانی: ۱۵ مهر ۱۴۰۲
زمان مطالعه: ۳ دقیقه
کاهش کد Boilerplate در رابط کاربری فلاتر — به زبان ساده

زمانی که مشغول ساخت رابط‌های کاربری موبایل هستیم، معمولاً لازم می‌شود که موقعیت‌های مشابهی را بارها و بارها مدیریت کنیم و صفحه‌ها و کامپوننت‌های خود را بسازیم. یک مثال عمده از چنین وضعیتی، زمانی است که بخواهیم اپلیکیشن با منابع داده ریموت ارتباط برقرار کند. در این موارد احتمالاً در هر صفحه باید نوعی نشانگر بارگذاری در زمان اجرا شدن درخواست شبکه نمایش دهیم. در این مقاله با روش کاهش کد Boilerplate در رابط کاربری فلاتر آشنا خواهیم شد.

فهرست مطالب این نوشته

در چنین موقعیت‌هایی که در پاراگراف قبل توصیف کردیم، می‌توان از BLoC Builder استفاده کرد. این سازنده‌ها برای پاسخ دادن به حالت‌هایی که از سوی BLoC -ها ارائه می‌شوند، طراحی شده‌اند و به این ترتیب UI می‌تواند به طرز مناسبی رندر شود. ما قصد داریم در این مقاله بر مبنای این مفهوم، سازنده‌های سفارشی BLoC ایجاد کنیم که حالت‌های رایج BLoC را مدیریت می‌کنند و موجب کاهش کد قالبی یا Boilerplate می‌شود.

با اینکه این رویکرد می‌تواند برای طیف بسیار متنوعی از مقاصد مفید باشد، ‌اما در این راهنما قصد داریم یک Builder سفارشی طراحی کنیم که به ما در مدیریت مشخصه‌های مرتبط با شبکه کمک می‌کند. سپس از این Builder جدید برای ایجاد یک صفحه ابتدایی کمک بگیریم که حالت‌های یک BLoC را که شامل کارکردهای شبیه‌سازی‌شده شبکه است، رندر خواهد کرد.

برای شروع کار باید یک BaseNetworkState بسازیم که حالت‌های BLoC ما را ایجاد می‌کند و می‌توان آن را بسط داد. این کلاس شامل مشخصه isFetching خواهد بود که برای نشان دادن این که مشغول اجرای درخواست ناهمگامی هستیم در UI مورد استفاده قرار می‌گیرد.

1class BaseNetworkState {
2  final bool isFetching;
3
4  BaseNetworkState({
5    this.isFetching: false,
6  });
7}

سپس یک HomeState ایجاد می‌کنیم که اقدام به بسط BaseNetworkState ایجاد شده می‌کند و یک مشخصه name اضافی به آن می‌افزاید که BLoC برای ما بازیابی خواهد کرد.

1class HomeState extends BaseNetworkState {
2  final String name;
3
4  HomeState({
5    this.name,
6    bool isFetching = false,
7  }) : super(isFetching: isFetching);
8}

اکنون زمان آن رسیده است که BLoC خود را بسازیم. این BLoC به رویداد FetchUser پاسخ خواهد داد و در آن ابتدا یک حالت با isFetching روی True تنظیم می‌شود تا UI بداند که یک عملیات ناهمگام در حال اجرا است. سپس BLoC شروع به شبیه‌سازی تأخیر شبکه از طریق ایجاد یک تأخیر مصنوعی می‌کند و در نهایت یک حالت شامل name مورد نظر بازیابی می‌شود.

1class HomeBloc extends Bloc<HomeEvent, HomeState> {
2  @override
3  HomeState get initialState => HomeState();
4
5  @override
6  Stream<HomeState> mapEventToState(HomeEvent event) async* {
7    if (event is HomeEventFetch) {
8      yield HomeState(isFetching: true);
9
10      yield await Future.delayed(
11        Duration(seconds: 2),
12        () => HomeState(name: 'Test'),
13      );
14    }
15  }
16}

اکنون می‌توانیم بر این مبنا یک NetworkBlocBuilder بسازیم که قرار است کامپوننت‌های UI مناسب را در پاسخ به مشخصه‌های BaseNetworkState رندر کنیم. در این مورد مسئول مدیریت رندر کردن نشانگر بارگذاری در زمانی که BLoC یک حالتی که isFetching به صورت True است. به صورت پیش‌فرض باید یک CircularProgressIndicator که به صورت مرکزگرا تنظیم شده است نشان دهیم، ‌اما می‌خواهیم این وضعیت را با پذیرش این گزینه به عنوان یک آرگومان اختیاری، سفارشی‌سازی کنیم.

1class NetworkBlocBuilder<B extends Bloc<dynamic, S>, S extends BaseNetworkState>
2    extends BlocBuilderBase<B, S> {
3  /// The [builder] function which will be invoked on each widget build.
4  /// The [builder] takes the `BuildContext` and current [state] and
5  /// must return a widget.
6  /// This is analogous to the [builder] function in [StreamBuilder].
7  final BlocWidgetBuilder<S> builder;
8
9  //The loading indicator will be shown when isFetching.
10  //Defaults to a CircularProgressIndicator.
11  final Widget loadingIndicator;
12
13  /// {@macro blocbuilder}
14  const NetworkBlocBuilder({
15    Key key,
16    @required this.builder,
17    this.loadingIndicator,
18    B bloc,
19    BlocBuilderCondition<S> condition,
20  })  : assert(builder != null),
21        super(key: key, bloc: bloc, condition: condition);
22
23  @override
24  Widget build(BuildContext context, S state) {
25    if (state.isFetching) {
26      return this.loadingIndicator ??
27          Center(child: CircularProgressIndicator());
28    }
29    return builder(context, state);
30  }
31}

این وضعیت از طریق یک بسط ساده BlocBuilderBase به دست می‌آید و تنها با حالت‌هایی تطبیق خواهد یافت که BaseNetworkState انشعاب می‌یابند.

در نهایت HomeScreen را می‌سازیم که از NetworkBlocBuilder استفاده می‌کند تا به رندر کردن حالت BLoC کمک کند. اینک معنی این حرف آن است که علی‌رغم این که HomeBLoC چند مشخصه حالت دارد، تنها باید در مورد مدیریت name نگران باشیم.

1class _HomeScreenState extends State<HomeScreen> {
2  HomeBloc _bloc;
3
4  @override
5  void initState() {
6    this._bloc = HomeBloc();
7    super.initState();
8  }
9
10  @override
11  void dispose() {
12    _bloc.close();
13    super.dispose();
14  }
15
16  @override
17  Widget build(BuildContext context) {
18    return Scaffold(
19      body: Center(
20        child: NetworkBlocBuilder<HomeBloc, HomeState>(
21            bloc: this._bloc,
22            builder: (context, state) {
23              if (state.name == null) {
24                return RaisedButton(
25                  child: Text('Fetch User'),
26                  onPressed: () => this._bloc.add(HomeEventFetch()),
27                );
28              }
29
30              return Text(state.name);
31            }),
32      ),
33    );
34  }
35}

بدین ترتیب پس از این که کاربر روی دکمه‌ای که رویداد FetchUser را تحریک می‌کند، کلیک کرد، یک نشانگر بارگذاری تا زمانی که name ارائه شود نمایش می‌یابد و نیاز به انجام هیچ کار اضافی در HomeScreen وجود ندارد.

کاهش کد Boilerplate در رابط کاربری فلاتر

سخن پایانی

رویکردی که در این راهنما معرفی شد، ‌به این مقدار محدود نمی‌شود و می‌توان آن را برای استفاده در یک قابلیت جالب دیگر کتابخانه BLoC یعنی شنونده‌های BLoC بسط داد. برای نمونه BaseNetworkState می‌تواند شامل یک مشخصه errorMessage باشد و یک مشخصه NetworkBlocListener مسئول نمایش کادر خطای مناسب در صورت وجود پیام باشد. به این ترتیب به پایان این مقاله می‌رسیم.

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۰ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
swlh
نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *