Pengubah Stream dan Penyahlantunan Peristiwa dalam BLoC
Gunakan pengubah serentak untuk mengehadkan kadar, menyah lantun dan menyusun peristiwa masuk.
Pengubah Stream dan Penyahlantunan Peristiwa dalam BLoC ialah pelajaran Pembangunan Aplikasi Mudah Alih Flutter percuma di CoddyKit. Ini ialah pelajaran 2 daripada 4. Sebanyak 3 pelajaran dalam laluan pembelajaran ini boleh dibaca sepenuhnya secara percuma — selepas itu, CoddyKit PRO membuka akses kepada semua pelajaran, serta latihan praktikal dengan penyunting kod terbina dalam dan tutor kecerdasan buatan yang tersedia 24/7. Pelajaran ini merupakan sebahagian daripada laluan pembelajaran Pembangunan Aplikasi Mudah Alih Flutter, dan kemajuan anda disegerakkan merentas web serta aplikasi CoddyKit. Kursus Pembangunan Aplikasi Mudah Alih Flutter merangkumi sejumlah 4 pelajaran.
Mengapa Keserentakan Peristiwa Penting
Dalam flutter_bloc, setiap panggilan kepada add(event) menghantar peristiwa ke dalam strim dalaman. Secara lalai, setiap pengendali peristiwa berjalan serentak apabila peristiwa tiba. Ini sesuai untuk kebanyakan peristiwa, tetapi sesetengah sumber menghasilkan peristiwa terlalu kerap:
- Bar carian menghasilkan peristiwa
TextChangedpada setiap ketukan kekunci. - Pendengar tatalan menghasilkan berpuluh-puluh peristiwa
ScrolledToBottomsesaat. - Butang boleh ditekan dengan pantas lalu menghasilkan peristiwa
SubmitPressedpendua.
Menghantar permintaan rangkaian bagi setiap ketukan kekunci membazirkan lebar jalur dan boleh memaparkan hasil yang lapuk. Pelajaran ini menunjukkan cara mendikit, menyah lantun dan menyusun peristiwa ini menggunakan pengubah strim dalam BLoC.
Parameter Pengubah
API on<Event> moden menerima argumen transformer pilihan. Pengubah ialah EventTransformer<E> — fungsi yang menerima Stream<E> peristiwa masuk dan pemeta, lalu mengembalikan strim yang telah diubah.
Dengan membekalkan pengubah, anda mengawal cara peristiwa jenis tersebut diproses: dinyah lantun, didikit, digugurkan atau dijalankan satu demi satu. Tandatangan tersebut ialah:
typedef EventTransformer<Event> = Stream<Event> Function(Stream<Event> events, EventMapper<Event> mapper);
Anda jarang menulis pengubah secara manual — pakej bloc_concurrency menyediakan pengubah yang biasa digunakan.
// Registering a handler with a custom transformer
on<SearchTermChanged>(
_onSearchTermChanged,
transformer: (events, mapper) => events
.debounceTime(const Duration(milliseconds: 300))
.switchMap(mapper),
);Nyah Lantun: Tunggu Jeda
Nyah lantun mengabaikan peristiwa sehingga tempoh senyap berlalu. Jika pengguna masih menaip, kami terus menetapkan semula pemasa; hanya apabila mereka berhenti, contohnya selama 300ms, kami memproses peristiwa terkini.
Ini pilihan yang tepat untuk kotak carian: anda mahu menyoal API selepas pengguna berhenti menaip, bukan pada setiap ketukan kekunci. Nyah lantun menggugurkan semua peristiwa perantaraan dan hanya mengekalkan peristiwa terakhir dalam setiap rentetan.
- Mengurangkan panggilan rangkaian dengan ketara.
- Mengorbankan sedikit kependaman (kelewatan nyah lantun) demi kecekapan.
Nyah Lantun dalam Dart Biasa
Sebelum memasukkannya ke dalam BLoC, berikut ialah idea nyah lantun yang dinyatakan sebagai atur cara Dart yang boleh dijalankan. Kami mensimulasikan ketukan kekunci yang tiba dengan jarak masa berbeza dan hanya mencetak istilah selepas pengguna berhenti menaip selama 300ms.
Operator RxDart melakukannya untuk anda, tetapi melihat logik pemasa membantu menjelaskan maksud nyah lantun.
import 'dart:async';
void main() async {
Timer? debounce;
String? pending;
final done = Completer<void>();
void onChanged(String term) {
pending = term;
debounce?.cancel();
debounce = Timer(const Duration(milliseconds: 300), () {
print('search: $pending');
if (pending == 'flutter') done.complete();
});
}
// Fast burst, then a pause, then more typing.
onChanged('f');
await Future.delayed(const Duration(milliseconds: 50));
onChanged('fl');
await Future.delayed(const Duration(milliseconds: 50));
onChanged('flu');
await Future.delayed(const Duration(milliseconds: 400)); // pause -> fires
onChanged('flutter');
await done.future;
}Pendikitan: Satu bagi Setiap Tetingkap
Pendikitan membenarkan peristiwa pertama diteruskan, kemudian mengabaikan peristiwa seterusnya untuk tempoh tetap. Berbeza daripada nyah lantun, pendikitan tidak menunggu jeda — ia memancarkan peristiwa dengan serta-merta, kemudian mengehadkan kadarnya.
Ini sesuai untuk tatalan tanpa penghujung dan tekanan butang pantas: anda mahu bertindak terhadap ScrolledToBottom pertama dengan segera, tetapi mengabaikan gelombang pendua yang mengikutinya semasa gerak isyarat tatalan yang sama.
throttleTimedengantrailing: false= hanya pinggir hadapan (bertindak sekarang, kemudian menyejuk).- Mengelakkan pemuatan halaman pendua atau penghantaran berganda.
// Infinite-scroll feed: react immediately, then cool down 500ms
on<FeedScrolledToEnd>(
_onScrolledToEnd,
transformer: (events, mapper) => events
.throttleTime(const Duration(milliseconds: 500))
.asyncExpand(mapper),
);Pengubah bloc_concurrency
Pakej rasmi bloc_concurrency menyediakan empat pengubah sedia digunakan yang mengawal cara peristiwa bertindih dikendalikan:
concurrent()— pengendali berjalan secara selari (lalai).sequential()— peristiwa beratur dan diproses dengan ketat satu demi satu.droppable()— semasa pengendali berjalan, peristiwa baharu daripada jenis tersebut dibuang.restartable()— peristiwa baharu membatalkan pengendali yang sedang berjalan dan bermula semula.
Pengubah ini boleh digabungkan dengan operator masa: contohnya, nyah lantun dahulu, kemudian restartable() untuk membatalkan carian yang lapuk.
import 'package:bloc_concurrency/bloc_concurrency.dart';
// Submit button: ignore extra taps while the first submit is in flight
on<FormSubmitted>(_onSubmit, transformer: droppable());
// Saving steps that must run in order
on<StepSaved>(_onStepSaved, transformer: sequential());Menggabungkan Nyah Lantun dengan restartable
Untuk BLoC carian, resipi yang ideal ialah nyah lantun kemudian restartable:
- Nyah lantun peristiwa
TextChangedsupaya anda hanya membuat pertanyaan selepas pengguna berhenti seketika. - restartable() supaya jika pertanyaan yang lebih baharu tiba semasa permintaan sebelumnya masih dimuatkan, permintaan lapuk itu dibatalkan dan tidak pernah menimpa hasil terbaharu.
Gabungan ini menghapuskan panggilan yang membazir dan respons yang tidak mengikut turutan. Anda membalut pengubah bloc_concurrency dengan pembantu kecil yang menggunakan debounceTime terlebih dahulu.
import 'package:bloc_concurrency/bloc_concurrency.dart';
import 'package:rxdart/rxdart.dart';
EventTransformer<E> debounceRestartable<E>(Duration duration) {
return (events, mapper) =>
restartable<E>().call(events.debounceTime(duration), mapper);
}
// Usage inside a Bloc constructor:
on<SearchTermChanged>(
_onSearchTermChanged,
transformer: debounceRestartable(const Duration(milliseconds: 300)),
);Bloc Carian Lengkap
Beginilah semua bahagian digabungkan dalam SearchBloc sebenar. Perhatikan bahawa pengendali ialah async dan boleh memancarkan beberapa keadaan dengan emit (memuatkan, kemudian hasil atau ralat). Oleh sebab pengubahnya ialah restartable, permintaan yang lapuk berhenti memancarkan keadaan sebaik sahaja istilah yang lebih baharu tiba.
class SearchBloc extends Bloc<SearchEvent, SearchState> {
final SearchRepository repo;
SearchBloc(this.repo) : super(const SearchState.initial()) {
on<SearchTermChanged>(
_onTermChanged,
transformer: debounceRestartable(const Duration(milliseconds: 300)),
);
}
Future<void> _onTermChanged(
SearchTermChanged event,
Emitter<SearchState> emit,
) async {
final term = event.term.trim();
if (term.isEmpty) {
emit(const SearchState.initial());
return;
}
emit(const SearchState.loading());
try {
final results = await repo.search(term);
emit(SearchState.success(results));
} catch (e) {
emit(SearchState.failure(e.toString()));
}
}
}Operator Pemetaan: switchMap berbanding asyncExpand
Dalam pengubah tersuai, anda menentukan cara pemeta digunakan pada setiap peristiwa:
switchMap(mapper)— membatalkan strim dalaman sebelumnya apabila peristiwa baharu tiba. Dari segi konsep, setara denganrestartable.exhaustMap(mapper)— mengabaikan peristiwa baharu semasa satu peristiwa sedang aktif. Setara dengandroppable.asyncExpand(mapper)— berjalan secara berjujukan; setiap peristiwa menunggu pengendali sebelumnya selesai. Setara dengansequential.flatMap(mapper)— semuanya berjalan serentak. Setara denganconcurrent.
Utamakan pengubah bloc_concurrency yang dinamakan demi kejelasan; gunakan RxDart mentah hanya apabila anda perlu menggabungkan masa dan pemetaan dalam satu ungkapan.
Demonstrasi Urutan Pendikitan dalam Dart
Contoh yang boleh dijalankan ini memodelkan pendikitan tanpa sebarang rangka kerja. Peristiwa pertama dalam setiap tetingkap 200ms diproses; peristiwa yang tiba semasa tempoh penyejukan digugurkan. Perhatikan bahawa hanya peristiwa pada pinggir hadapan yang kekal.
import 'dart:async';
void main() async {
DateTime? lastAccepted;
const window = Duration(milliseconds: 200);
final accepted = <int>[];
void onEvent(int id) {
final now = DateTime.now();
if (lastAccepted == null || now.difference(lastAccepted!) >= window) {
lastAccepted = now;
accepted.add(id);
}
}
// Fire 6 events; some land inside the cooldown window.
for (var i = 1; i <= 6; i++) {
onEvent(i);
await Future.delayed(const Duration(milliseconds: 90));
}
// Only leading-edge events per 200ms window are kept.
print('accepted: $accepted');
}Memilih Strategi yang Tepat
Padankan transformer dengan niat pengguna:
- Carian / autolengkap: debounce + restartable — tunggu jeda, batalkan pertanyaan lama.
- Pemuatan halaman tatal tanpa penghujung: throttle + droppable — muat sekali, abaikan tindakan lain dalam gerak isyarat tersebut.
- Penghantaran borang / pembayaran: droppable — halang penghantaran pendua semasa satu penghantaran sedang diproses.
- Penulisan mengikut tertib (langkah penyimpanan, analitik): sequential — kekalkan tertib tanpa pertindihan.
Sentiasa tambahkan bloc_concurrency dan rxdart pada pubspec.yaml, dan ingat bahawa transformer hanya mempengaruhi jenis peristiwa yang didaftarkan padanya.
Semakan Pantas
Uji pemahaman anda tentang senario kotak carian.
Rumusan
Anda telah mempelajari cara mengawal keserentakan peristiwa dalam BLoC dengan transformer strim:
- Pengendali
on<Event>menerimatransformeryang membentuk semula strim peristiwa masuk. - Debounce menunggu jeda (sesuai untuk carian); throttle bertindak pada permulaan, kemudian menyejuk seketika (sesuai untuk tatal dan ketikan pantas).
bloc_concurrencymenyediakanconcurrent,sequential,droppable, danrestartable, yang sepadan denganflatMap,asyncExpand,exhaustMap, danswitchMapdalam RxDart.- Resipi carian standard ialah debounce kemudian restartable; penghantaran borang menggunakan droppable; penulisan mengikut tertib menggunakan sequential.
Pilih transformer yang sepadan dengan niat pengguna, dan ingat bahawa ia hanya digunakan pada jenis peristiwa yang didaftarkan padanya.
Pelajari Dart dengan tutor kecerdasan buatan — percuma
Tulis dan jalankan kod sebenar dalam pelayar anda, dapatkan bantuan segera daripada tutor kecerdasan buatan yang tersedia 24/7, dan sambung semula dari tempat anda berhenti di web atau dalam aplikasi.
- Kursus
- 22
- Pelajaran
- 88
Soalan Lazim
Adakah pelajaran “Pengubah Stream dan Penyahlantunan Peristiwa dalam BLoC” percuma?
Ya — sebanyak 3 pelajaran dalam laluan pembelajaran Pembangunan Aplikasi Mudah Alih Flutter, termasuk “Pengubah Stream dan Penyahlantunan Peristiwa dalam BLoC”, boleh dibaca sepenuhnya secara percuma di web ini. Selepas itu, CoddyKit PRO membuka akses kepada semua pelajaran, serta latihan interaktif dengan penyunting kod terbina dalam dan tutor kecerdasan buatan yang tersedia 24/7. Kursus Pembangunan Aplikasi Mudah Alih Flutter merangkumi sejumlah 4 pelajaran.
Apakah yang akan saya pelajari dalam “Pengubah Stream dan Penyahlantunan Peristiwa dalam BLoC”?
Gunakan pengubah serentak untuk mengehadkan kadar, menyah lantun dan menyusun peristiwa masuk. Anda berlatih Pembangunan Aplikasi Mudah Alih Flutter menggunakan kod praktikal yang dijalankan terus dalam pelayar, manakala tutor kecerdasan buatan 24/7 menjawab soalan anda semasa anda mengikuti pelajaran.
Adakah saya memerlukan pengalaman untuk memulakan Pembangunan Aplikasi Mudah Alih Flutter?
Tiada pengalaman terdahulu diperlukan. Pembelajaran Pembangunan Aplikasi Mudah Alih Flutter di CoddyKit disusun untuk pelajar daripada peringkat pemula hingga lanjutan, jadi anda boleh bermula di sini atau dari awal dan belajar mengikut kadar anda sendiri. Ini ialah pelajaran 2 daripada 4.
Berapa lamakah pelajaran “Pengubah Stream dan Penyahlantunan Peristiwa dalam BLoC” diambil?
Kebanyakan pelajaran CoddyKit mengambil masa kira-kira 5–10 minit. Setiap pelajaran ringkas dan interaktif, jadi anda boleh membuat kemajuan secara berterusan dan menyambung tepat dari tempat anda berhenti di web atau aplikasi.
Bolehkah saya menulis dan menjalankan kod dalam pelajaran Pembangunan Aplikasi Mudah Alih Flutter ini?
Ya. Setiap pelajaran Pembangunan Aplikasi Mudah Alih Flutter menyertakan penyunting kod terbina dalam, jadi anda boleh menulis dan menjalankan kod sebenar terus dalam pelayar serta menerima maklum balas kecerdasan buatan serta-merta — tanpa memerlukan persediaan setempat.
Semua pelajaran dalam kursus ini
- Peristiwa, Keadaan dan Keputusan Cubit-berbanding-Bloc
- Pengubah Stream dan Penyahlantunan Peristiwa dalam BLoC
- Mengekalkan Keadaan dengan HydratedBloc
- Menguji BLoC dengan bloc_test dan Mocktail