به حداکثر رساندن عملکرد برنامه Flutter با (Async)NotifierProvider، فریز شده و تولید کننده کد Riverpod
به حداکثر رساندن عملکرد برنامه Flutter برای ارائه یک تجربه کاربری یکپارچه بسیار مهم است. بهعنوان توسعهدهندگان نرمافزار، ما دائماً به دنبال ابزارهایی هستیم که میتوانند تجربه کدنویسی ما را افزایش دهند و در عین حال کارایی و کیفیت کد ما را بهبود بخشند. این راهنمای مبتدی بر استفاده از قدرت AsyncNotifierProvider و NotifierProvider از Riverpod، همراه با تولید کننده کد Riverpod، برای مدیریت کارآمد حالت تمرکز دارد. با ترکیب این ابزارها، میتوانید ارائهدهندگان را سریعتر تولید کنید، فرآیند ارسال ویژگی ref را سادهتر کنید و اشکالزدایی را ساده کنید. این راهنما شامل مثالهای سادهای است که نشان میدهد چگونه از این ارائهدهندگان در پروژه خود استفاده کنید و از مزایای تولیدکنندههای کد فریز شده و ریورپاد استفاده کنید.
پیش نیازها
-
دانش پایه دارت
-
درک پایه ای از فلاتر و مدیریت دولتی.
-
یک ویرایشگر کد (اندروید استودیو یا VScode توصیه می شود)
-
یک دستگاه تلفن همراه یا شبیه ساز برای ساخت.
-
شما اولین مقاله این مجموعه را خوانده اید یا حداقل دانش اولیه ای از نحوه کار ریورپاد دارید 👇🏽
محدوده این آموزش
در این آموزش به موارد زیر می پردازیم:
-
مثال هایی برای نشان دادن اجرای Notifier و AsyncNotifier Provider.
-
نحوه استفاده از ابزارهای تولید کد مانند فریز شده و تولید کننده کد Riverpod.
-
نحوه استفاده
AsyncValue
برای رسیدگی به وضعیت بارگذاری -
نحوه استفاده
copyWith
هنگام کار با یک کلاس تغییرناپذیر.
نصب وابستگی ها
اول، شما باید به خود برسید pubspec.yaml
و بسته های زیر را اضافه کنید
dependencies:
flutter_riverpod: ^2.1.3
riverpod_annotation: ^1.1.1
freezed_annotation: ^2.2.0
freezed: ^2.3.2
dev_dependencies:
build_runner:
riverpod_generator: ^1.1.1
سپس اجرا کنید flutter pub get
شما با موفقیت وابستگی های لازم را به پروژه خود اضافه کرده اید.
چرا از ابزارهای تولید کد استفاده کنیم؟
اگر انتخاب کنید که بدون ابزارهای تولید کد کار کنید، ارائه دهندگان Riverpod شما همچنان کاملاً کاربردی خواهند بود. با این حال، Riverpod استفاده از ابزارهای تولید کد را به شدت توصیه می کند. تولید کد از ابزاری برای تولید کد استفاده می کند. در دارت، پس از اضافه کردن نحو تولید کد و کامپایل، کد شما به طور خودکار تولید می شود. انجام این کار باعث صرفه جویی در زمان و انرژی شما می شود که برای نوشتن آن کد استفاده می کردید، به خصوص زمانی که روی یک پروژه بزرگ کار می کنید و نیاز به انجام مکرر آن وظایف دارید. تولید کد به جلوگیری از خطاهایی که ممکن است هنگام انجام مکرر یک کار اتفاق بیفتد کمک می کند. اشکال زدایی را بهتر می کند و به طور کلی زندگی شما را آسان تر می کند.
مولد کد فریز شده
Freezed یک بسته تولید کد است که به شما کمک می کند تا کلاس های داده را با دارت ایجاد کنید. با استفاده از Freezed میتوانید مدلها، اتحادیهها و موارد دیگر تولید کنید. Freezed به شما امکان می دهد به جای نوشتن خطوط طولانی کد که ممکن است مستعد خطا باشد، روی تعریف کلاس داده خود تمرکز کنید.
درباره کارهایی که می توانید با Freezed انجام دهید بیشتر بخوانید.
مولد کد ریورپاد
با بسته تولید کد Riverpod، اکنون اعلام ارائه دهندگان آسان تر است. دیگر نیازی نیست که ارائه دهندگان خود را به صورت دستی بنویسید یا تعجب کنید که کدام ارائه دهنده خاص برای مورد استفاده شما مناسب است.
تنها کاری که باید انجام دهید این است که برای تعریف مولد کد Riverpod خود، دستور زبان را دنبال کنید و کد خود را حاشیه نویسی کنید، سپس با build_runner
شما می توانید تمام ارائه دهندگان خود را ایجاد کنید.
به عنوان مثال، این سینتکس تولید کد برای این ارائه دهندگان مختلف Riverpod است
برای ارائه دهندگان:
@riverpod
int foo(FooRef ref) => 0;
برای FutureProviders:
@riverpod
Future<int> foo(FooRef ref) async {
return 0;
}
برای State Providers
@riverpod
class Foo extends _$Foo {
@override
int build() => 0;
}
می توانید متوجه شوید که الگویی برای ایجاد ارائه دهندگان وجود دارد. هنگامی که سینتکس صحیح را قرار دادید، می توانید ارائه دهندگان را دقیقاً مانند آن ایجاد کنید.
به ارائه دهنده اطلاع دهید
Riverpod 2.0 با اضافه شدن دو نوع ارائه دهنده جدید، NotifierProvider و AsyncNotifierProvider عرضه شد. Riverpod توصیه می کند که به جای ارائه دهندگان ChangeNotifier و StateNotifier از NotifierProvider استفاده کنید، بنابراین ما روی این دو تمرکز می کنیم.
NotifierProvider برای گوش دادن و افشای یک Notifier استفاده می شود. یک Notifier حالتی را نشان می دهد که می تواند در طول زمان تغییر کند.
بیایید یک مثال ساده را در نظر بگیریم 👇🏽
ما یک مولد رشته تصادفی با دو دکمه می سازیم، یکی برای تولید یک رشته جدید و دیگری برای پاک کردن کل لیست رشته ها. در این مثال، ما از مولد کد Riverpod نیز استفاده خواهیم کرد.
-
ابتدا با استفاده از حاشیه نویسی Riverpod و نحو کد زیر، یک NotifierProvider ایجاد می کنیم. ما همچنین توابع اضافه کردن یک رشته تصادفی و پاک کردن لیست رشته ها را اضافه می کنیم. با مشخص کردن با نام فایلی که باید تولید شود را اضافه می کنیم
part
همانطور که در کد مشاهده می شود.توجه: نام فایلی که باید تولید شود با نام فایل فعلی که روی آن کار می کنید یکسان است. هنگام مشخص کردن آن با
part
باید اضافه کنید.g.dart
همانطور که فایل های تولید شده توسط Riverpod نامگذاری می شوند.
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'notifier_list_provider.g.dart';
@riverpod
class RandomStrNotifier extends _$RandomStrNotifier{
@override
List<String> build() {
return [];
}
//using Dart's spread operator we create a new copy of the list
void addString(String randomStr){
state = [...state, randomStr];
}
void removeStrings(){
state = [];
}
}
از کد ما، می توانیم ببینیم که RandomStrNotifier
یک لیست خالی برمی گرداند، سپس دو تابع را اضافه کردیم تا به لیست اضافه شود و لیست پاک شود. NotifierProvider و AsyncNotifierProvider از حالت تغییرناپذیر پشتیبانی می کنند، چون وضعیت ما تغییرناپذیر است، نمی توانیم بگوییم state.add
یا state.remove
. بنابراین ما یک کپی جدید از لیست ایجاد می کنیم. state
برای به روز رسانی حالت رابط کاربری استفاده می شود.
برای اجرای کد مولد این دستور را در ترمینال اجرا کنید:
flutter pub run build_runner watch --delete-conflicting-outputs
پس از اجرای موفقیت آمیز، می توانید فایل ارائه دهنده تولید شده خود را در تب پروژه خود، در ویرایشگر کد خود مشاهده کنید.
از تصویر بالا می توانید ببینید که فایل تولید شده چگونه خواهد بود.
توجه: در صورت دریافت خطا Could not find a file named "pubspec.yaml" in "C:\Users\…
سپس اجرا کنید dart pub get
دستور روی ترمینال شما
- در ادامه، بیایید این ارائه دهنده و توابع را به رابط کاربری خود اضافه کنیم
Widget build(BuildContext context, ref) {
// rebuid the widget when there is a change
List<String> randomStrList = ref.watch(randomStrNotifierProvider);
final random = Random();
return Scaffold(
appBar: AppBar(
title: const Text("RiverPod Notifier Example App"),
backgroundColor: Colors.brown,
),
body: SingleChildScrollView(
child: Column(
children: [
Column(
children: [
//map to a list
...randomStrList.map((string) =>
Container(
alignment: Alignment.center,
margin: const EdgeInsets.only(bottom: 10,top: 5),
height: 30,
width: 300,
color: Colors.brown,
child: Text(string.toString(),
style: const TextStyle(
color: Colors.white
),
)))
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton.icon(
icon: const Icon(Icons.add),
label: const Text('Generate'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.brown, // Background color
),
onPressed: () {
//add string to list function
ref.read(randomStrNotifierProvider.notifier).addString("This is the "
"random String ${5 + random.nextInt( 1000 + 1 - 5)}");
},
),
ElevatedButton.icon(
icon: const Icon(Icons.clear),
label: const Text('Clear'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.brown, // Background color
),
onPressed: () {
//clear list function
ref.read(randomStrNotifierProvider.notifier).removeString();
},
)
],
)
],
),
),
);
}
همانطور که می بینید مولد کد Riverpod، یک تطابق ایجاد کرد randomStrNotifierProvider
وقتی برنامه را اجرا می کنید باید به این شکل باشد👇🏽
AsyncNotifierProvider
AsyncNotifierProvider برای گوش دادن و افشای یک asyncNotifier استفاده می شود. AsyncNotifier یک اعلان کننده است که به صورت ناهمزمان مقداردهی اولیه می شود.
بیایید به مثال شیرجه بزنیم
ما در حال ساخت یک برنامه ساده خواهیم بود که لیستی از محصولات را پس از مدت زمان 3 ثانیه با یک دکمه برای پاک کردن لیست محصولات بارگیری می کند.
- ابتدا از ابزار Freezed code generator برای ایجاد کلاس محصول خود استفاده می کنیم. سپس با استفاده از
copyWith
روش ما اشیاء محصول را ایجاد خواهیم کرد که در لیست ما قرار خواهند گرفت. با مشخص کردن با نام فایلی که باید تولید شود را اضافه می کنیمpart
همانطور که در کد مشاهده می شود. نام فایل تولید شده نام فعلی فایل شما و.freezed.dart
نام گذاری فایل های فریز شده به این صورت است.
import 'package:freezed_annotation/freezed_annotation.dart';
//replace with part 'name_of_your_file.freezed.dart';
part 'async_notifier_list_provider.freezed.dart';
@freezed
class Product with _$Product{
const Product._();
const factory Product({
String? name,
String? description,
}) = _Product;
}
const Product _product1 = Product(name: "Dart course for beginners",
description: "This is course will make you a dart star");
final Product _product2 = _product1.copyWith(description: "This course will make you a pro");
final Product _product3 = _product1.copyWith(name: "Ultimate Dart course for beginners");
final products = [
_product1,
_product2,
_product3,
];
ما می توانیم این دستور را در ترمینال اجرا کنیم تا فایل فریز شده را تولید کنیم
flutter pub run build_runner watch --delete-conflicting-outputs
تصویر بالا نشان می دهد که فایل فریز شده تولید شده چگونه به نظر می رسد.
ما استفاده کردیم copyWith
روشی برای ایجاد اشیاء جدیدی از محصول که به لیست اضافه کردیم. را copyWith
متد برای برگرداندن یک شی جدید با همان ویژگی های اصلی استفاده می شود، اما با مقادیری که شما مشخص کرده اید، هنگام کار با ساختارهای تغییرناپذیر مانند Freezed استفاده می شود.
اکنون ما به جلو می رویم و کلاس ارائه دهنده Riverpod خود را اضافه می کنیم، که پس از 3 ثانیه لیست محصولات را دریافت می کند. ما همچنین عملکرد را اضافه می کنیم تا لیست محصولات را نیز پاک کنیم.
//replace with part 'name_of_your_file.g.dart';
part 'async_notifier_list_provider.g.dart';
@riverpod
class AsyncProducts extends _$AsyncProducts {
Future<List<Product>> _fetchProducts() async {
await Future.delayed(const Duration(seconds: 3));
return products;
}
@override
FutureOr<List<Product>> build() async {
return _fetchProducts();
}
Future<void>clearProducts()async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() async{
await Future.delayed(const Duration(seconds: 3));
return [];
});
}
}
asyncNotifierProvider لیست آینده محصولات را برمی گرداند. برای تغییر رابط کاربری، اکنون آن را ایجاد می کنیم clearProducts
تابع، با استفاده از AsyncValue class
ما می توانیم به راحتی بارگذاری، وضعیت خطا و وضعیت داده را مدیریت کنیم.
نگاه کردن به AsyncValue
کلاس، AsyncValue.guard
برای تبدیل آیندهای که ممکن است شکست بخورد به چیزی امن برای خواندن استفاده میشود، توصیه میشود به جای تلاش و گرفتن بلوکها از آن استفاده کنید. هر دو حالت داده و خطا را مدیریت می کند.
در مرحله بعد، این دستور را در ترمینال اجرا کنید تا کد Freezed و Riverpod لازم را ایجاد کنید
flutter pub run build_runner watch --delete-conflicting-outputs
بیایید این ارائه دهنده و عملکرد را به رابط کاربری خود اضافه کنیم
Widget build(BuildContext context, WidgetRef ref) {
final productProvider = ref.watch(asyncProductsProvider);
return Scaffold(
appBar: AppBar(
title: const Text("AsyncNotifier"),
actions: [
IconButton(
icon: const Icon(
Icons.clear,
color: Colors.white,
),
onPressed: () {
ref.read(asyncProductsProvider.notifier).clearProducts();
},
)
]
),
body: Container(
child: productProvider.when(
data: (products)=> ListView.builder(
itemCount: products.length,
itemBuilder: (context, index){
return Padding(
padding: const EdgeInsets.only(left: 10,right: 10,top: 10),
child: Card(
color: Colors.blueAccent,
elevation: 3,
child: ListTile(
title: Text("${products[index].name}",style: const TextStyle(
color: Colors.white, fontSize: 15)),
subtitle: Text("${products[index].description}",style: const TextStyle(
color: Colors.white, fontSize: 15)),
),
),
);
}),
error: (err, stack) => Text("Error: $err",style: const TextStyle(
color: Colors.white, fontSize: 15)),
loading: ()=> const Center(child: CircularProgressIndicator(color: Colors.blue,)),
),
),
);
}
در اینجا ما می توانیم آن را با تماس مشاهده کنیم ref.watch
میتوانیم به ارائهدهنده خود دسترسی پیدا کنیم، سپس تابع محصول شفاف را به آن اضافه میکنیم onPressed
با تماس ref.read
. اکنون میتوانیم با استفاده از حالتهای مختلف پاسخ مدیریت کنیم productProvider.when
وقتی برنامه را اجرا می کنید باید به این شکل باشد👇🏽
خلاصه
ما به پایان این آموزش رسیدیم. در اینجا آموختیم که:
-
NotifierProvider به یک اعلان کننده گوش می دهد و در معرض نمایش قرار می دهد، زمانی که تغییری در وضعیت وجود دارد، به طور خودکار UI را به روز می کند. AsyncNotifierProvider برای گوش دادن و افشای اعلان کننده ای که به طور ناهمزمان مقداردهی اولیه شده است استفاده می شود.
-
با استفاده از ابزارهای تولید کد مانند تولیدکننده کد فریز شده و ریورپاد، میتوانیم به راحتی دادهها و کلاسهای ارائهدهنده را با کد بسیار کم تولید کنیم.
-
متد copyWith برای ایجاد یک شی جدید اما با مقادیری که شما مشخص کرده اید، هنگام کار با کلاس های تغییرناپذیر استفاده می شود.
-
در نهایت، کلاس AsyncValue برای مدیریت کارآمد داده ها، بارگذاری و حالت های خطا استفاده می شود.
نتیجه
تبریک می گوییم، شما با موفقیت یاد گرفتید که چگونه از جدیدترین ارائه دهندگان Riverpod، NotifierProvider و AsyncNotifierProvider برای مدیریت وضعیت، نحوه استفاده از ابزارهای تولید کد برای تولید کد، و نحوه استفاده از روش copyWith و کلاس AsyncValue استفاده کنید. اگر از این مقاله لذت بردید، بهتر است واکنش نشان دهید و برای مطالب بیشتر من را دنبال کنید. اگر سؤالی دارید یا هر گونه خطایی دارید، لطفاً بازخورد خود را ارسال کنید.
منابع
Riverpod Docs