рд╕реНрдкрдВрджрди рдХреНрд▓рд╛рдЗрдВрдЯ рд╕рд░реНрд╡рд░ рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдЙрджрд╛рд╣рд░рдг



рдЗрд╕ рдЯреНрдпреВрдЯреЛрд░рд┐рдпрд▓ рдореЗрдВ, рд╣рдо рдПрдХ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд╡рд┐рдХрд╕рд┐рдд рдХрд░рдиреЗ рдЬрд╛ рд░рд╣реЗ рд╣реИрдВ рдЬреЛ рдЗрдВрдЯрд░рдиреЗрдЯ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдбреЗрдЯрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЗрд╕реЗ рдПрдХ рд╕реВрдЪреА рдореЗрдВ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рддрд╛ рд╣реИред рдХреБрдЫ рдЗрд╕ рддрд░рд╣



рдареАрдХ рд╣реИ, рдЪрд▓реЛ рдПрдХ рдкрд░рд┐рдпреЛрдЬрдирд╛ рдмрдирд╛рдХрд░ рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВред рдХрдорд╛рдВрдб рд▓рд╛рдЗрди рдкрд░ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд▓рд┐рдЦреЗрдВ

flutter create flutter_infinite_list 

рдЗрд╕рдХреЗ рдмрд╛рдж, рд╣рдорд╛рд░реЗ рдирд┐рд░реНрднрд░рддрд╛ рдлрд╝рд╛рдЗрд▓ pubspec.yaml рдкрд░ рдЬрд╛рдПрдВ рдФрд░ рд╣рдореЗрдВ рдЬреЛ рднреА рдЬрд╝рд░реВрд░рдд рд╣реИ рдЙрд╕реЗ рдЬреЛрдбрд╝реЗрдВ

 name: flutter_infinite_list description: A new Flutter project. version: 1.0.0+1 environment: sdk: ">=2.0.0-dev.68.0 <3.0.0" dependencies: flutter: sdk: flutter flutter_bloc: 0.4.11 http: 0.12.0 equatable: 0.1.1 dev_dependencies: flutter_test: sdk: flutter flutter: uses-material-design: true 

рдЙрд╕рдХреЗ рдмрд╛рдж, рдирд┐рдореНрди рдХрдорд╛рдВрдб рдХреЗ рд╕рд╛рде рдЗрди рдирд┐рд░реНрднрд░рддрд╛рдУрдВ рдХреЛ рд╕реНрдерд╛рдкрд┐рдд рдХрд░реЗрдВ

 flutter packages get 

рдЗрд╕ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЗ рд▓рд┐рдП, рд╣рдо рдЬреЛрд╕рд╛ рдкреНрд▓реЗрд╕рд╣реЛрд▓реНрдбрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдореЛрдЪрд╛ рдбреЗрдЯрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд░реЗрдВрдЧреЗред рдпрджрд┐ рдЖрдк рдЗрд╕ рд╕реЗрд╡рд╛ рд╕реЗ рдкрд░рд┐рдЪрд┐рдд рдирд╣реАрдВ рд╣реИрдВ, рддреЛ рдпрд╣ рдПрдХ рдСрдирд▓рд╛рдЗрди REST API рд╕реЗрд╡рд╛ рд╣реИ рдЬреЛ рдирдХрд▓реА рдбреЗрдЯрд╛ рднреЗрдЬ рд╕рдХрддреА рд╣реИред рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдкреНрд░реЛрдЯреЛрдЯрд╛рдЗрдк рдХреЗ рдирд┐рд░реНрдорд╛рдг рдХреЗ рд▓рд┐рдП рдпрд╣ рдмрд╣реБрдд рдЙрдкрдпреЛрдЧреА рд╣реИред

рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд▓рд┐рдВрдХ рдХреЛ рдЦреЛрд▓рдХрд░ jsonplaceholder.typicode.com/posts?_start=0&_limit=2 рдЖрдк JSON рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рджреЗрдЦреЗрдВрдЧреЗ рдЬрд┐рд╕рдХреЗ рд╕рд╛рде рд╣рдо рдХрд╛рдо рдХрд░реЗрдВрдЧреЗред

 [ { "userId": 1, "id": 1, "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto" }, { "userId": 1, "id": 2, "title": "qui est esse", "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla" } ] 

рдзреНрдпрд╛рди рджреЗрдВ рдХрд┐ рд╣рдорд╛рд░реЗ GET рдЕрдиреБрд░реЛрдз рдореЗрдВ рд╣рдордиреЗ рдкреИрд░рд╛рдореАрдЯрд░ рдХреЗ рд░реВрдк рдореЗрдВ рдкреНрд░рд╛рд░рдВрдн рдФрд░ рд╕рдорд╛рдкреНрддрд┐ рдмрд╛рдзрд╛ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХреА рд╣реИред

рдорд╣рд╛рди, рдЕрдм рд╣рдо рдЬрд╛рдирддреЗ рд╣реИрдВ рдХрд┐ рд╣рдорд╛рд░реЗ рдбреЗрдЯрд╛ рдХреА рд╕рдВрд░рдЪрдирд╛ рдХреИрд╕реА рджрд┐рдЦреЗрдЧреА! рдЖрдЗрдП рдЙрдирдХреЗ рд▓рд┐рдП рдПрдХ рдореЙрдбрд▓ рдмрдирд╛рдПрдВред

рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд╕рд╛рдордЧреНрд░реА рдХреЗ рд╕рд╛рде рдПрдХ рдкреЛрд╕реНрдЯред рдбрд╛рд░реНрдЯ рдлрд╝рд╛рдЗрд▓ рдмрдирд╛рдПрдБ

 import 'package:equatable/equatable.dart'; class Post extends Equatable { final int id; final String title; final String body; Post({this.id, this.title, this.body}) : super([id, title, body]); @override String toString() => 'Post { id: $id }'; } 

рдкреЛрд╕реНрдЯ рдХреЗрд╡рд▓ рдЖрдИрдбреА, рд╢реАрд░реНрд╖рдХ рдФрд░ рд╢рд░реАрд░ рдХреЗ рд╕рд╛рде рдПрдХ рд╡рд░реНрдЧ рд╣реИред рд╣рдо рдмрд╛рдж рдореЗрдВ рдПрдХ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд╕реНрдЯреНрд░рд┐рдВрдЧ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕реНрдЯреНрд░реЛрд╕реНрдЯрд┐рдВрдЧ рдлрд╝рдВрдХреНрд╢рди рдХреЛ рднреА рдУрд╡рд░рд░рд╛рдЗрдб рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рд╣рдо рдЗрдХреНрд╡рд┐рдЯреЗрдмрд▓ рдХреНрд▓рд╛рд╕ рдХрд╛ рд╡рд┐рд╕реНрддрд╛рд░ рдХрд░рддреЗ рд╣реИрдВ рддрд╛рдХрд┐ рд╣рдо рдкреЛрд╕реНрдЯ рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕ рдХреА рддреБрд▓рдирд╛ рдХрд░ рд╕рдХреЗрдВред

рдЕрдм рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рд╕рд░реНрд╡рд░ рд╕реЗ рдПрдХ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдореЙрдбрд▓ рд╣реИ, рдЪрд▓реЛ рд╡реНрдпрд╛рдкрд╛рд░ рддрд░реНрдХ (рдмрд┐рдЬрдиреЗрд╕ рд▓реЙрдЬрд┐рдХ рдШрдЯрдХ (рдмреНрд▓реЙрдХ)) рдХреЛ рд▓рд╛рдЧреВ рдХрд░рддреЗ рд╣реИрдВред

рдЗрд╕рд╕реЗ рдкрд╣рд▓реЗ рдХрд┐ рд╣рдо рдЕрдиреБрдкреНрд░рдпреЛрдЧ рд╡рд┐рдХрд╛рд╕ рдореЗрдВ рдЧреЛрддрд╛ рд▓рдЧрд╛рдПрдБ, рд╣рдореЗрдВ рдпрд╣ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдХрд┐ рд╣рдорд╛рд░рд╛ рдкреЛрд╕реНрдЯрдмреНрд▓реЙрдХ рдХреНрдпрд╛ рдХрд░реЗрдЧрд╛ред

рд╢реАрд░реНрд╖ рд╕реНрддрд░ рдкрд░, рд╡рд╣ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреНрд░рд┐рдпрд╛рдУрдВ рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдиреЗ (рд╕реНрдХреНрд░реЙрд▓ рдХрд░рдиреЗ) рдФрд░ рдирдП рдкрджреЛрдВ рдХреЛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЬрд┐рдореНрдореЗрджрд╛рд░ рд╣реЛрдЧрд╛ рдЬрдм рдкреНрд░рд╕реНрддреБрддрд┐ рдкрд░рдд рдЙрдирд╕реЗ рдЕрдиреБрд░реЛрдз рдХрд░рддреА рд╣реИред рдЖрдЗрдП рдЗрд╕реЗ рд▓рд╛рдЧреВ рдХрд░рдирд╛ рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВред

рд╣рдорд╛рд░рд╛ PostBloc рдХреЗрд╡рд▓ рдПрдХ рдШрдЯрдирд╛ рдкрд░ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рджреЗрдЧрд╛ред рдбреЗрдЯрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рдЬреЛ рд╕реНрдХреНрд░реАрди рдкрд░ рдЖрд╡рд╢реНрдпрдХрддрд╛рдиреБрд╕рд╛рд░ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред рдЪрд▓рд┐рдП рдПрдХ рд╡рд░реНрдЧ post_event.dart рдмрдирд╛рддреЗ рд╣реИрдВ рдФрд░ рд╣рдорд╛рд░реЗ рдИрд╡реЗрдВрдЯ рдХреЛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рдХрд░рддреЗ рд╣реИрдВред

 import 'package:equatable/equatable.dart'; abstract class PostEvent extends Equatable {} class Fetch extends PostEvent { @override String toString() => 'Fetch'; } 

рдлрд┐рд░ рд╕реЗ, рд╣рдорд╛рд░реЗ рдШрдЯрдирд╛ рдХреЛ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рдиреЗ рд╡рд╛рд▓реА рд▓рд╛рдЗрди рдХреЛ рдФрд░ рдЕрдзрд┐рдХ рдЖрд╕рд╛рдиреА рд╕реЗ рдкрдврд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдлрд┐рд░ рд╕реЗ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░реЗрдВред рд╣рдореЗрдВ рд╡рд╕реНрддреБрдУрдВ рдХреА рддреБрд▓рдирд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЗрдХреНрд╡рд┐рдЯреЗрдмрд▓ рдХреНрд▓рд╛рд╕ рдХрд╛ рд╡рд┐рд╕реНрддрд╛рд░ рдХрд░рдиреЗ рдХреА рднреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред

рд╕рд╛рд░рд╛рдВрд╢ рдореЗрдВ, рд╣рдорд╛рд░реЗ PostBloc PostEvents рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВрдЧреЗ рдФрд░ рдЙрдиреНрд╣реЗрдВ PostStates рдореЗрдВ рдмрджрд▓ рджреЗрдВрдЧреЗред рд╣рдордиреЗ рд╕рднреА PostEvents (Fetch) рдЗрд╡реЗрдВрдЯ рд╡рд┐рдХрд╕рд┐рдд рдХрд┐рдП рд╣реИрдВ, рд╣рдо PostState рдкрд░ рдЖрдЧреЗ рдмрдврд╝реЗрдВрдЧреЗред

рд╣рдорд╛рд░реА рдкреНрд░рд╕реНрддреБрддрд┐ рдкрд░рдд рдореЗрдВ рд╕рд╣реА рдврдВрдЧ рд╕реЗ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрдИ рд░рд╛рдЬреНрдп рд╣реЛрдиреЗ рдЪрд╛рд╣рд┐рдПред

isInitializing - рдкреНрд░рд╕реНрддреБрддрд┐ рдкрд░рдд рдХреЛ рд╕реВрдЪрд┐рдд рдХрд░рддрд╛ рд╣реИ рдХрд┐ рдбреЗрдЯрд╛ рд▓реЛрдб рд╣реЛ рд░рд╣рд╛ рд╣реИ, рдЬрдмрдХрд┐ рдПрдХ рд▓реЛрдбрд┐рдВрдЧ рд╕рдВрдХреЗрддрдХ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реИред

рдкреЛрд╕реНрдЯ - рдкреЛрд╕реНрдЯ рдСрдмреНрдЬреЗрдХреНрдЯ рдХреА рдПрдХ рд╕реВрдЪреА рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рддрд╛ рд╣реИ

isrror - рдЙрд╕ рд▓реЗрдпрд░ рдХреЛ рд╕реВрдЪрд┐рдд рдХрд░рддрд╛ рд╣реИ рдЬреЛ рдбреЗрдЯрд╛ рд▓реЛрдб рдХрд░рддреЗ рд╕рдордп рд╣реБрдИ

hasRededMax - рдЙрдкрд▓рдмреНрдз рдкрд┐рдЫрд▓реЗ рд░рд┐рдХреЙрд░реНрдб рдХрд╛ рд╕рдВрдХреЗрдд

рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд╕рд╛рдордЧреНрд░реА рдХреЗ рд╕рд╛рде рдкреЛрд╕реНрдЯ_рд╕реНрдЯреИрдЯ.рдбрд╛рд░реНрдЯ рдХреНрд▓рд╛рд╕ рдмрдирд╛рдПрдВ

 import 'package:equatable/equatable.dart'; import 'package:flutter_infinite_list/models/models.dart'; abstract class PostState extends Equatable { PostState([Iterable props]) : super(props); } class PostUninitialized extends PostState { @override String toString() => 'PostUninitialized'; } class PostInitialized extends PostState { final List<Post> posts; final bool hasError; final bool hasReachedMax; PostInitialized({ this.hasError, this.posts, this.hasReachedMax, }) : super([posts, hasError, hasReachedMax]); factory PostInitialized.success(List<Post> posts) { return PostInitialized( posts: posts, hasError: false, hasReachedMax: false, ); } factory PostInitialized.failure() { return PostInitialized( posts: [], hasError: true, hasReachedMax: false, ); } PostInitialized copyWith({ List<Post> posts, bool hasError, bool hasReachedMax, }) { return PostInitialized( posts: posts ?? this.posts, hasError: hasError ?? this.hasError, hasReachedMax: hasReachedMax ?? this.hasReachedMax, ); } @override String toString() => 'PostInitialized { posts: ${posts.length}, hasError: $hasError, hasReachedMax: $hasReachedMax }'; } 

рд╣рдордиреЗ рд╕реБрд╡рд┐рдзрд╛ рдФрд░ рдкрдардиреАрдпрддрд╛ рдХреЗ рд▓рд┐рдП рдлреИрдХреНрдЯрд░реА рдкреИрдЯрд░реНрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ред PostState рд╕рдВрд╕реНрдерд╛рдУрдВ рдХреЛ рдореИрдиреНрдпреБрдЕрд▓ рд░реВрдк рд╕реЗ рдмрдирд╛рдиреЗ рдХреЗ рдмрдЬрд╛рдп, рд╣рдо рд╡рд┐рднрд┐рдиреНрди рдХрд╛рд░рдЦрд╛рдиреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдЬреИрд╕реЗ PostState.initial ()

рдЕрдм рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдИрд╡реЗрдВрдЯреНрд╕ рдФрд░ рд╕реНрдерд┐рддрд┐рдпрд╛рдВ рд╣реИрдВ, рд╣рдорд╛рд░реЗ PostBloc рдХреЛ рдмрдирд╛рдиреЗ рдХрд╛ рд╕рдордп рд╣реИ
рд╕рд░рд▓ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдорд╛рд░реЗ PostBloc рдореЗрдВ рдПрдХ рдкреНрд░рддреНрдпрдХреНрд╖ http рдХреНрд▓рд╛рдЗрдВрдЯ рдирд┐рд░реНрднрд░рддрд╛ рд╣реЛрдЧреА, рд╣рд╛рд▓рд╛рдВрдХрд┐ рдЙрддреНрдкрд╛рджрди рдореЗрдВ рдЖрдкрдХреЛ рдЗрд╕реЗ рдПрдкреА рдХреНрд▓рд╛рдЗрдВрдЯ рдореЗрдВ рдмрд╛рд╣рд░реА рдирд┐рд░реНрднрд░рддрд╛ рдореЗрдВ рд▓рдкреЗрдЯрдирд╛ рдЪрд╛рд╣рд┐рдП рдФрд░ рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рдкреИрдЯрд░реНрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдПред

Post_bloc.dart рдмрдирд╛рдПрдВ

 import 'package:bloc/bloc.dart'; import 'package:meta/meta.dart'; import 'package:http/http.dart' as http; import 'package:flutter_infinite_list/bloc/bloc.dart'; import 'package:flutter_infinite_list/models/models.dart'; class PostBloc extends Bloc<PostEvent, PostState> { final http.Client httpClient; PostBloc({@required this.httpClient}); @override // TODO: implement initialState PostState get initialState => null; @override Stream<PostState> mapEventToState( PostState currentState, PostEvent event, ) async* { // TODO: implement mapEventToState yield null; } } 

рдзреНрдпрд╛рди рджреЗрдВ рдХрд┐ рдХреЗрд╡рд▓ рд╣рдорд╛рд░реА рдХрдХреНрд╖рд╛ рдХреА рдШреЛрд╖рдгрд╛ рд╕реЗ рд╣рдо рдпрд╣ рдХрд╣ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдпрд╣ PostEvents рдХреЛ рд╕реНрд╡реАрдХрд╛рд░ рдХрд░реЗрдЧрд╛ рдФрд░ PostStates рджреЗрдЧрд╛

рдЪрд▓реЛ рдЖрд░рдВрднрд┐рдХ рд╡рд┐рдХрд╛рд╕ рдХреЛ рдЖрдЧреЗ рдмрдврд╝рд╛рддреЗ рд╣реИрдВред

 import 'package:bloc/bloc.dart'; import 'package:meta/meta.dart'; import 'package:http/http.dart' as http; import 'package:flutter_infinite_list/bloc/bloc.dart'; import 'package:flutter_infinite_list/models/models.dart'; class PostBloc extends Bloc<PostEvent, PostState> { final http.Client httpClient; PostBloc({@required this.httpClient}); @override PostState get initialState => PostState.initial(); @override Stream<PostState> mapEventToState( PostState currentState, PostEvent event, ) async* { // TODO: implement mapEventToState yield null; } } 

рдЕрдЧрд▓рд╛, рдЖрдкрдХреЛ mapEventToState рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рдЬреЛ рд╣рд░ рдмрд╛рд░ рдХрд┐рд╕реА рдИрд╡реЗрдВрдЯ рдХреЛ рднреЗрдЬреЗ рдЬрд╛рдиреЗ рдкрд░ рдЖрдЧ рд▓рдЧрд╛рдПрдЧрд╛ред

 import 'dart:convert'; import 'package:meta/meta.dart'; import 'package:http/http.dart' as http; import 'package:bloc/bloc.dart'; import 'package:flutter_infinite_list/bloc/bloc.dart'; import 'package:flutter_infinite_list/models/models.dart'; class PostBloc extends Bloc<PostEvent, PostState> { final http.Client httpClient; PostBloc({@required this.httpClient}); @override get initialState => PostState.initial(); @override Stream<PostState> mapEventToState(currentState, event) async* { if (event is Fetch && !currentState.hasReachedMax) { try { final posts = await _fetchPosts(currentState.posts.length, 20); if (posts.isEmpty) { yield currentState.copyWith(hasReachedMax: true); } else { yield PostState.success(currentState.posts + posts); } } catch (_) { yield PostState.failure(); } } } Future<List<Post>> _fetchPosts(int startIndex, int limit) async { final response = await httpClient.get( 'https://jsonplaceholder.typicode.com/posts?_start=$startIndex&_limit=$limit'); if (response.statusCode == 200) { final data = json.decode(response.body) as List; return data.map((rawPost) { return Post( id: rawPost['id'], title: rawPost['title'], body: rawPost['body'], ); }).toList(); } else { throw Exception('error fetching posts'); } } } 

рдЕрдм рд╣рд░ рдмрд╛рд░ PostEvent рднреЗрдЬ рджрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЕрдЧрд░ рдпрд╣ рдПрдХ рдирдореВрдирд╛рдХрд░рдг рдШрдЯрдирд╛ рд╣реИ рдФрд░ рд╣рдо рд╕реВрдЪреА рдХреЗ рдЕрдВрдд рддрдХ рдирд╣реАрдВ рдкрд╣реБрдВрдЪреЗ рд╣реИрдВ, рддреЛ рдЕрдЧрд▓реА 20 рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐рдпрд╛рдВ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХреА рдЬрд╛рдПрдВрдЧреАред

рдЪрд▓рд┐рдП рд╣рдорд╛рд░реЗ PostBloc рдХреЛ рдереЛрдбрд╝рд╛ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░рддреЗ рд╣реИрдВ

 import 'dart:convert'; import 'package:meta/meta.dart'; import 'package:rxdart/rxdart.dart'; import 'package:http/http.dart' as http; import 'package:bloc/bloc.dart'; import 'package:flutter_infinite_list/bloc/bloc.dart'; import 'package:flutter_infinite_list/models/models.dart'; class PostBloc extends Bloc<PostEvent, PostState> { final http.Client httpClient; PostBloc({@required this.httpClient}); @override Stream<PostEvent> transform(Stream<PostEvent> events) { return (events as Observable<PostEvent>) .debounce(Duration(milliseconds: 500)); } @override get initialState => PostState.initial(); @override Stream<PostState> mapEventToState(currentState, event) async* { if (event is Fetch && !currentState.hasReachedMax) { try { final posts = await _fetchPosts(currentState.posts.length, 20); if (posts.isEmpty) { yield currentState.copyWith(hasReachedMax: true); } else { yield PostState.success(currentState.posts + posts); } } catch (_) { yield PostState.failure(); } } } Future<List<Post>> _fetchPosts(int startIndex, int limit) async { final response = await httpClient.get( 'https://jsonplaceholder.typicode.com/posts?_start=$startIndex&_limit=$limit'); if (response.statusCode == 200) { final data = json.decode(response.body) as List; return data.map((rawPost) { return Post( id: rawPost['id'], title: rawPost['title'], body: rawPost['body'], ); }).toList(); } else { throw Exception('error fetching posts'); } } } 

рдорд╣рд╛рди, рд╣рдордиреЗ рд╡реНрдпрд╛рд╡рд╕рд╛рдпрд┐рдХ рддрд░реНрдХ рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЛ рдкреВрд░рд╛ рдХрд┐рдпрд╛ рд╣реИ!

рд╣рдорд╛рд░реЗ UI рдХреЛ рдЖрдХрд░реНрд╖рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╡рд░реНрдЧ main.dart рдмрдирд╛рдПрдБ рдФрд░ рдЙрд╕рдореЗрдВ runApp рд▓рд╛рдЧреВ рдХрд░реЗрдВ

 import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Infinite Scroll', home: Scaffold( appBar: AppBar( title: Text('Posts'), ), body: HomePage(), ), ); } } 

рдЕрдЧрд▓рд╛, рдПрдХ HomePage рдмрдирд╛рдПрдВ рдЬреЛ рд╣рдорд╛рд░реА рдкреЛрд╕реНрдЯ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рддрд╛ рд╣реИ рдФрд░ PostBloc рд╕реЗ рдХрдиреЗрдХреНрдЯ рд╣реЛрддрд╛ рд╣реИ

 class HomePage extends StatefulWidget { @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { final _scrollController = ScrollController(); final PostBloc _postBloc = PostBloc(httpClient: http.Client()); final _scrollThreshold = 200.0; _HomePageState() { _scrollController.addListener(_onScroll); _postBloc.dispatch(Fetch()); } @override Widget build(BuildContext context) { return BlocBuilder( bloc: _postBloc, builder: (BuildContext context, PostState state) { if (state.isInitializing) { return Center( child: CircularProgressIndicator(), ); } if (state.isError) { return Center( child: Text('failed to fetch posts'), ); } if (state.posts.isEmpty) { return Center( child: Text('no posts'), ); } return ListView.builder( itemBuilder: (BuildContext context, int index) { return index >= state.posts.length ? BottomLoader() : PostWidget(post: state.posts[index]); }, itemCount: state.hasReachedMax ? state.posts.length : state.posts.length + 1, controller: _scrollController, ); }, ); } @override void dispose() { _postBloc.dispose(); super.dispose(); } void _onScroll() { final maxScroll = _scrollController.position.maxScrollExtent; final currentScroll = _scrollController.position.pixels; if (maxScroll - currentScroll <= _scrollThreshold) { _postBloc.dispatch(Fetch()); } } } 

рдЕрдЧрд▓рд╛, рд╣рдо рдмреЙрдЯрдорд▓реИрдбрд░ рд▓рд╛рдЧреВ рдХрд░рддреЗ рд╣реИрдВ, рдЬреЛ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЛ рдирдП рдкрджреЛрдВ рдХреЗ рд▓реЛрдбрд┐рдВрдЧ рдХреЛ рджрд┐рдЦрд╛рдПрдЧрд╛ред

 class BottomLoader extends StatelessWidget { @override Widget build(BuildContext context) { return Container( alignment: Alignment.center, child: Center( child: SizedBox( width: 33, height: 33, child: CircularProgressIndicator( strokeWidth: 1.5, ), ), ), ); } } 

рдЕрдВрдд рдореЗрдВ, рд╣рдо рдПрдХ PostWidget рд▓рд╛рдЧреВ рдХрд░рддреЗ рд╣реИрдВ рдЬреЛ рдЯрд╛рдЗрдк рдкреЛрд╕реНрдЯ рдХреЗ рдПрдХ рд╣реА рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЛ рдЖрдХрд░реНрд╖рд┐рдд рдХрд░реЗрдЧрд╛

 class PostWidget extends StatelessWidget { final Post post; const PostWidget({Key key, @required this.post}) : super(key: key); @override Widget build(BuildContext context) { return ListTile( leading: Text( post.id.toString(), style: TextStyle(fontSize: 10.0), ), title: Text('${post.title}'), isThreeLine: true, subtitle: Text(post.body), dense: true, ); } } 

рдмрд╕ рдЗрддрдирд╛ рд╣реА, рдЕрдм рдЖрдк рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛ рдЪрд▓рд╛ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рдкрд░рд┐рдгрд╛рдо рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВред

рдкреНрд░реЛрдЬреЗрдХреНрдЯ рд╕реНрд░реЛрддреЛрдВ рдХреЛ рдЬреАрдердм рдкрд░ рдбрд╛рдЙрдирд▓реЛрдб рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ

Source: https://habr.com/ru/post/hi435688/


All Articles