স্কিপ করে মূল কন্টেন্ট এ যাও

Provider

সমস্ত প্রভাইডারের মধ্যে Provider হল সবচেয়ে মৌলিক। এটি একটি মান তৈরি করে... এবং এতটুকুই।

Provider সাধারণত ব্যবহৃত হয়:

  • ক্যাশিং গণনা
  • অন্য প্রভাইডারের কাছে একটি ভ্যালু প্রকাশ করা (যেমন একটি Repository/HttpClient)।
  • একটি ভ্যালু ওভাররাইড করার জন্য টেস্ট বা উইজেটগুলির জন্য একটি উপায় প্রদান করে৷
  • select ব্যবহার না করেই প্রদানকারী/উইজেটগুলির পুনর্নির্মাণ হ্রাস করা।

ক্যাশে গণনা করতে Provider ব্যবহার করা হয়

ref.watch এর সাথে মিলিত হলে সিঙ্ক্রোনাস ক্রিয়াকলাপ ক্যাশ করার জন্য প্রোভাইডার একটি শক্তিশালী টুল।

একটি উদাহরণ টোডোগুলির একটি তালিকা ফিল্টার করা হবে। যেহেতু একটি তালিকা ফিল্টার করা কিছুটা ব্যয়বহুল হতে পারে, আমরা আদর্শভাবে যখনই আমাদের অ্যাপ্লিকেশন পুনরায় রেন্ডার হয় তখন আমরা আমাদের টোডো তালিকা ফিল্টার করতে চাই না। এই পরিস্থিতিতে, আমরা আমাদের জন্য ফিল্টারিং করতে Provider ব্যবহার করতে পারি।

এর জন্য, অনুমান করুন যে আমাদের অ্যাপ্লিকেশনটিতে একটি বিদ্যমান StateNotifierProvider রয়েছে যা করণীয়গুলির একটি তালিকা পরিচালনা করেঃ


class Todo {
Todo(this.description, this.isCompleted);
final bool isCompleted;
final String description;
}

class TodosNotifier extends StateNotifier<List<Todo>> {
TodosNotifier() : super([]);

void addTodo(Todo todo) {
state = [...state, todo];
}
// TODO add other methods, such as "removeTodo", ...
}

final todosProvider = StateNotifierProvider<TodosNotifier, List<Todo>>((ref) {
return TodosNotifier();
});

সেখান থেকে, আমরা Provider ব্যবহার করতে পারি টোডোর ফিল্টার করা তালিকা প্রকাশ করতে, শুধুমাত্র সম্পূর্ণ করা টোডোগুলিকে দেখায়ঃ


final completedTodosProvider = Provider<List<Todo>>((ref) {
// আমরা todosProvider থেকে সব টুডু ধারণ করব
final todos = ref.watch(todosProvider);

// আর আমরা শুধু completed টুডু রিটার্ন করব
return todos.where((todo) => todo.isCompleted).toList();
});

এই কোডের সাহায্যে, আমাদের UI এখন completedTodosProvider লিসেন করে সম্পূর্ণ টোডোর তালিকা দেখাতে সক্ষমঃ

Consumer(builder: (context, ref, child) {
final completedTodos = ref.watch(completedTodosProvider);
// TODO show the todos using a ListView/GridView/.../* SKIP */
return Container();
/* SKIP END */
});

আকর্ষণীয় অংশ হল, তালিকা ফিল্টারিং এখন ক্যাশে করা হয়েছে।

এর অর্থ হল যে সম্পূর্ণ টোডোর তালিকা পর্যন্ত পুনঃগণনা করা হবে না যথক্ষন না todos যোগ/মুছে ফেলা/আপডেট করা হয়, এমনকি আমরা একাধিকবার সম্পূর্ণ করা টোডোর তালিকা পড়লেও।

নোট করুন কিভাবে আমাদের ক্যাশে ম্যানুয়ালি অকার্যকর করার দরকার নেই যখন todos তালিকা পরিবর্তন হয়। Provider স্বয়ংক্রিয়ভাবে জানতে সক্ষম হয় কখন ফলাফল পুনরায় গণনা করতে হবে ধন্যবাদ ref.watch-কে।

Provider ব্যবহার করে প্রভাইডার/উইজেট পুনর্নির্মাণ হ্রাস করা

Provider এর একটি অনন্য দিক হল যে এমনকি যখন Provider পুনরায় গণনা করা হয় (সাধারণত ref.watch ব্যবহার করার সময়), এটি ভ্যালু পরিবর্তন না হওয়া পর্যন্ত এটি শোনে বা পড়ে এমন উইজেট/প্রোভাইডারদের আপডেট করবে না।

একটি বাস্তব বিশ্বের উদাহরণ একটি পৃষ্ঠাবদ্ধ দৃশ্যের পূর্ববর্তী/পরবর্তী বোতামগুলি সক্ষম/অক্ষম করার জন্য হবে:

stepper example

আমাদের ক্ষেত্রে, আমরা বিশেষভাবে "আগের" বোতামে ফোকাস করব। এই ধরনের বোতামের একটি সাদাসিধে বাস্তবায়ন একটি উইজেট হবে যা বর্তমান পৃষ্ঠা সূচী প্রাপ্ত করে এবং যদি সেই সূচকটি 0 এর সমান হয়, আমরা বোতামটি নিষ্ক্রিয় করব।

এই কোড হতে পারে:


final pageIndexProvider = StateProvider<int>((ref) => 0);

class PreviousButton extends ConsumerWidget {
const PreviousButton({super.key});


Widget build(BuildContext context, WidgetRef ref) {
// প্রথম পৃষ্ঠায় না থাকলে, পূর্ববর্তী বোতামটি সক্রিয়
final canGoToPreviousPage = ref.watch(pageIndexProvider) == 0;

void goToPreviousPage() {
ref.read(pageIndexProvider.notifier).update((state) => state - 1);
}

return ElevatedButton(
onPressed: canGoToPreviousPage ? goToPreviousPage : null,
child: const Text('previous'),
);
}
}

এই কোডের সমস্যা হল যে যখনই আমরা বর্তমান পৃষ্ঠা পরিবর্তন করি, "আগের" বোতামটি পুনর্নির্মাণ করবে। আদর্শ বিশ্বে, আমরা সক্রিয় এবং নিষ্ক্রিয় করার মধ্যে পরিবর্তন করার সময়ই বোতামটি পুনর্নির্মাণ করতে চাই।

এখানে সমস্যার মূল হল যে ব্যবহারকারীকে "আগের" বোতামের মধ্যে সরাসরি পূর্ববর্তী পৃষ্ঠায় যাওয়ার অনুমতি দেওয়া হয়েছে কিনা তা আমরা গণনা করছি।

এটি সমাধান করার একটি উপায় হল এই লজিকটিকে উইজেটের বাইরে এবং একটি Provider'-এ বের করাঃ


final pageIndexProvider = StateProvider<int>((ref) => 0);

// একটি প্রভাইডার যা ব্যবহারকারীকে পূর্ববর্তী পৃষ্ঠায় যাওয়ার অনুমতি দেওয়া হয়েছে কিনা তা গণনা করে
final canGoToPreviousPageProvider = Provider<bool>((ref) {
return ref.watch(pageIndexProvider) == 0;
});

class PreviousButton extends ConsumerWidget {
const PreviousButton({super.key});


Widget build(BuildContext context, WidgetRef ref) {
// আমরা এখন আমাদের নতুন প্রভাইডার দেখতেছি
// আমাদের উইজেট আর হিসাব করছে না আমরা আগের পৃষ্ঠায় যেতে পারব কিনা।
final canGoToPreviousPage = ref.watch(canGoToPreviousPageProvider);

void goToPreviousPage() {
ref.read(pageIndexProvider.notifier).update((state) => state - 1);
}

return ElevatedButton(
onPressed: canGoToPreviousPage ? goToPreviousPage : null,
child: const Text('previous'),
);
}
}

এই ছোট রিফ্যাক্টরিং করার মাধ্যমে, Provider-কে ধন্যবাদ পৃষ্ঠার সূচী পরিবর্তিত হলে আমাদের PreviousButton উইজেটটি আর পুনর্নির্মাণ করা হবে না।

এখন থেকে পৃষ্ঠার সূচী পরিবর্তন হলে, আমাদের canGoToPreviousPageProvider প্রভাইডার পুনরায় গণনা করা হবে। কিন্তু যদি প্রভাইডারের দ্বারা প্রকাশ করা ভ্যালু পরিবর্তন না হয়, তাহলে PreviousButton পুনর্নির্মাণ হবে না।

এই পরিবর্তনটি আমাদের বোতামের কার্যকারিতা উভয়ই উন্নত করেছে এবং আমাদের উইজেটের বাইরে লজিক বের করার আকর্ষণীয় সুবিধা পেয়েছে।