3
votes

Flutter Bloc ne met pas à jour l'état / ne fonctionne pas

Je développe une application mobile avec Flutter. J'utilise le package flutter block, https://pub.dev/packages/flutter_bloc pour gérer et configurer le bloc. Mais lorsque l'état change, cela ne met pas à jour les widgets ou les vues.

J'ai un fichier de classe de bloc appelé home_bloc.dart avec l'implémentation suivante.

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: BlocProvider(
        create: (context) => HomeBloc(),
        child: HomePage(),
      ),
    );
  }
}

C'est ma classe de référentiel renvoyant des données factices.

class HomePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _HomePageState();
  }
}

class _HomePageState extends State<HomePage> {
  IconData _searchIcon = Icons.search;
  Widget _appBarTitle;
  HomeBloc _homeBloc;

  @override
  void initState() {
    super.initState();
    this._homeBloc = BlocProvider.of(context);
    WidgetsBinding.instance.addPostFrameCallback((_) => this.fetchArticles());
    WidgetsBinding.instance
        .addPostFrameCallback((_) => this.buildAppBarTitle());
  }

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<HomeBloc, HomeState>(
      builder: (context, state) {
        return Scaffold(
          appBar: AppBar(title: Text("Home"),),
          body: Container(
            child: buildListView(context, state),
          ),
        );
      },
    );
  }

  @override
  void dispose() {
    super.dispose();
  }

  void buildAppBarTitle() {
    this.setState(() {
      if (_searchIcon == Icons.search) {
        this._appBarTitle = Text("Home");
      } else {
        this._appBarTitle = TextField(
          onChanged: (String inputValue) {
            debugPrint("Search term has changed $inputValue");
            //homeBloc.fetchArticles(filter: inputValue);
          },
          style: TextStyle(
            color: Colors.white,
          ),
          decoration: InputDecoration(
            hintText: "Search",
          ),
        );
      }
    });
  }

  Widget buildAppBarSearchIcon() {
    return IconButton(
        icon: Icon(
          _searchIcon,
          color: Colors.white,
        ),
        onPressed: () {
          if (this._searchIcon == Icons.search) {
            //display the search text field and close icons

            this.setState(() {
              this._searchIcon = Icons.close;
              this.buildAppBarTitle();
              //homeBloc.toggleFiltering();
            });
          } else {
            this.fetchArticles();
            this.setState(() {
              this._searchIcon = Icons.search;
              this.buildAppBarTitle();
              //homeBloc.toggleFiltering();
            });
          }
        });
  }

  Widget buildListView(
      BuildContext context, HomeState state) {
    if (state.articles.length > 0) {
      var listView = ListView.builder(
          itemCount: state.articles.length,
          itemBuilder: (context, index) {
            var item = state.articles[index];

            if (item is String) {
              return buildListFirstInitialView(item);
            }

            Article article = item as Article;

            return buildListArticleView(article);
          });

      return listView;
    } else {
      return Center(
        child: Text("No resources found."),
      );
    }
  }

  Widget buildListFirstInitialView(String initial) {
    return ListTile(
      title: Text(initial),
    );
  }

  Widget buildListArticleView(Article article) {
    return ListTile(
      title: Text(article.title),
    );
  }

  Widget buildBottomNavigationBar() {
    return BottomNavigationBar(
        currentIndex: 0,
        onTap: (int position) {},
        items: [
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            title: Text('Home'),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.settings),
            title: Text('Settings'),
          ),
        ]);
  }

  void fetchArticles({String filter = ""}) {
    HomeEvent event = HomeEvent();
    event.event = HomeEvent.FETCH_ARTICLES;
    _homeBloc.add(event);
  }
}

Ceci est mon widget HomePage

class Repository {
  Future<List<dynamic>> getArticles() async {
    List<dynamic> list = List<dynamic>();
    list.add("A");

    Article article1 = Article();
    article1.id = 1;
    article1.title = "A start is born";
    list.add(article1);

    Article article2 = Article();
    article2.id = 2;
    article2.title = "Asking for help";
    list.add(article2);

    Article article3 = Article();
    article3.id = 3;
    article3.title = "Angel is comming";
    list.add(article3);

    list.add("B");

    Article article4 = Article();
    article4.id = 4;
    article4.title = "Baby Boss";
    list.add(article4);

    Article article5 = Article();
    article5.id = 5;
    article5.title = "Beginner guide to Staying at Home";
    list.add(article5);

    list.add("C");

    Article article6 = Article();
    article6.id = 6;
    article6.title = "Care each other";
    list.add(article6);

    Article article7 = Article();
    article7.id = 7;
    article7.title = "Controlling the world";
    list.add(article7);

    Article article8 = Article();
    article8.id = 8;
    article8.title = "Chasing the dream";
    list.add(article8);

    return list;
  }
}

Comme vous pouvez le voir, c'est mon widget HomePage. Il récupérera les articles une fois le widget construit. Ensuite, la vue de liste sera mise à jour avec les données factices.

Ceci est mon fichier main.dart.

class HomeEvent {
  static const int FETCH_ARTICLES = 1;
  static const int TOGGLE_IS_FILTERING = 2;
  int _event = 0;
  String _filterKeyword = "";

  int get event => _event;

  void set event(int event) {
    this._event = event;
  }

  String get filterKeyword => _filterKeyword;

  void set filterKeyword(String filterKeyword) {
    this._filterKeyword = filterKeyword;
  }
}

class HomeBloc extends Bloc<HomeEvent, HomeState> {
  Repository _repository = Repository();
  HomeState state = HomeState();

  @override
  HomeState get initialState => state;

  @override
  Stream<HomeState> mapEventToState(HomeEvent event) async* {
    switch (event.event) {
      case HomeEvent.FETCH_ARTICLES:
        {
          List<dynamic> articles = List<dynamic>();
          fetchArticles(filter: event.filterKeyword).listen((dynamic article) {
            articles.add(article);
          });
          state = state.copyWith(articles: articles);
          break;
        }
      case HomeEvent.TOGGLE_IS_FILTERING:
        {
          state.isFiltering = ! state.isFiltering;
          state = state.copyWith();
          break;
        }
      default:
        {
          state = state.initial();
          break;
        }
    }

    yield state;
  }

  Stream<dynamic> fetchArticles({String filter = ""}) async* {
    List<dynamic> list = (this.state.articles.length > 0)
        ? this.state.articles
        : await _repository.getArticles();
    if (filter.isNotEmpty) {
      for (var article in list) {
        if (article is String) {
          yield article;
        } else if (article.title.contains(filter)) {
          yield article;
        }
      }
    } else {
      for (var article in list) {
        yield article;
      }
    }
  }
}

class HomeState {
  bool _isFiltering = false;
  List<dynamic> _articles = List<dynamic>();

  bool get isFiltering => _isFiltering;

  void set isFiltering(bool isFiltering) {
    this._isFiltering = isFiltering;
  }

  List<dynamic> get articles => _articles;

  void set articles(List<dynamic> list) {
    this._articles = list;
  }

  HomeState initial() {
    HomeState state = HomeState();
    state.isFiltering = false;
    state.articles = List<dynamic>();

    return state;
  }

  HomeState copyWith({ bool isFiltering, List<dynamic> articles }) {
    HomeState state = HomeState();
    state.isFiltering = isFiltering != null? isFiltering: this._isFiltering;
    state.articles = articles!=null && articles.length > 0? articles: this._articles;

    return state;
  }
}

Lorsque j'exécute mon application, cela ne met pas à jour la vue de liste avec les données factices. Au lieu de cela, il affiche toujours le message pour aucun enregistrement trouvé.

entrez la description de l'image ici

Pourquoi ça ne marche pas?


0 commentaires

3 Réponses :


1
votes

Bloc ne changera jamais Si l'état n'a pas changé, vous pourriez être confus que vous avez attribué un état, mais le fait est que Bloc est un Stream , vous devrez yield un état au lieu de l'attribuer directement.

J'espère que vous avez implémenté un copyWith , vous pouvez donc le faire comme ci-dessous:

// state = state.copyWith(articles: articles);
newState = state.copyWith(articles: articles);
...
yield newState;

Pour conserver votre structure, vous pouvez toujours utiliser:

yield state.copyWith(articles: articles)

Étant donné que state variable d' state est utilisée par Bloc, vous devez utiliser une autre variable pour empêcher Bloc de comparer la même variable ( state == l' state sera toujours vrai, donc l'état n'a jamais changé.)


2 commentaires

Salut, merci d'avoir publié la réponse. J'ai essayé. Mais cela ne fonctionne toujours pas.


Semble avoir plusieurs problèmes dans votre code, vous avez fait une chose étrange dans FETCH_ARTICLES , vous n'obtiendrez jamais la valeur parce que vous écoutez un flux qui n'est pas rempli avant de passer le state.copyWith(articles: articles) . C'est votre deuxième problème. Il est recommandé d' print toutes les étapes de débogage dans Bloc, afin que vous trouviez le problème lié ou non à Bloc . Utilisez await pour Si vous voulez vraiment utiliser stream avec Bloc.



0
votes

votre classe d'état n'est pas comparable ... Dart ne peut pas dire quand l'état a changé

Vous devez utiliser: import 'package: equatable / equatable.dart';

Je ne pense pas non plus que vous devriez définir la propriété state sur votre classe de bloc. Vous ne devriez que le céder et laisser le bloc le mettre à jour ...

s'il vous plaît vérifier les documents car je peux me tromper


0 commentaires

0
votes

J'ai eu le même problème et j'ai résolu ce problème en changeant:

yield status.toList();

à

yield status; 

À la fin de ma méthode mapEventToState() .

Et vous avez probablement dû faire pour tout rendement qui passe une liste.

Si cela a fonctionné pour vous faites le moi savoir


0 commentaires