admin管理员组

文章数量:1415645

I'm building a social feed. I have a view for the list of posts, and a view for creating a post. When the user creates a post, I want to submit it to my backend, make a callback to my setState callback in the views where posts are listed, and then pop the new post view off the widget tree with the result being that the post list view has been rebuilt, and in doing do, rebuilt the future builder getting new posts from the backend. For some reason, That's not working and I'm not sure why. I've even tried breaking the riverpod invocation into it's own function in my list posts class so that something would have to be explicitly invoked but it's not working.

social_feed.dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:irc/features/social/views/social_post_create.dart';

//Import Providers
import 'package:irc/shared/pocketbase_provider.dart';

//Import Feature Widgets
import 'package:irc/features/social/widgets/social_tile.dart';

class SocialFeedScaffold extends ConsumerStatefulWidget {
  const SocialFeedScaffold({super.key});
  @override
  ConsumerState<SocialFeedScaffold> createState() =>
      _SocialFeedScaffoldConsumerState();
}

class _SocialFeedScaffoldConsumerState
    extends ConsumerState<SocialFeedScaffold> {
  void setStateCallback() => setState(() {});

  @override
  Widget build(BuildContext context) {
    final pocketbase = ref.watch(pocketbaseProvider);
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: SingleChildScrollView(
            child: Column(
          children: [
            Padding(
              padding: const EdgeInsets.all(16.0),
              child: Text("What other researchers are talking about...",
                  style: GoogleFonts.cardo(
                      fontSize: 32, fontWeight: FontWeight.bold)),
            ),
            // INLINE SOCIAL FEED
            FutureBuilder(
                future: pocketbase.collection('posts').getList(
                    perPage: 100, sort: '-created', expand: 'posted_by'),
                builder: (BuildContext context, AsyncSnapshot snapshot) {
                  if (snapshot.hasData) {
                    List postList = snapshot.data!.toJson()['items'];
                    return ListView.separated(
                        shrinkWrap: true,
                        itemCount: postList.length,
                        separatorBuilder: (context, index) =>
                            const SizedBox(height: 16),
                        itemBuilder: (BuildContext context, int index) {
                          return SocialTile(
                              postContent: postList[index]['post_content'],
                              postedBy: snapshot.data!.toJson()['items'][index]
                                  ['expand']['posted_by']['username']);
                        });
                  } else if (snapshot.connectionState ==
                      ConnectionState.waiting) {
                    return const CircularProgressIndicator();
                  } else if (snapshot.hasError) {
                    return Text(snapshot.error.toString());
                  } else {
                    return const CircularProgressIndicator();
                  }
                }),
          ],
        )),
      ),
      floatingActionButton: FloatingActionButton(
          backgroundColor: Colors.black,
          child: const Icon(Icons.post_add, color: Colors.white),
          onPressed: () {
            Navigator.of(context).push(MaterialPageRoute(
                builder: (context) => NewPostFormView(
                      setStateCallback: setStateCallback,
                    )));
          }),
    );
  }
}

social_post_create.dart

import 'package:flutter/material.dart';
// You're not importing FLUTTER RIVERPOD, just riverpod and that's fricking your code.
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:irc/shared/pocketbase_provider.dart';
import 'package:pocketbase/pocketbase.dart';
import 'dart:ui';

class NewPostFormView extends ConsumerStatefulWidget {
  // Class Constructor
  const NewPostFormView({super.key, required this.setStateCallback});

  // Class Fields
  final void setStateCallback;
  @override
  ConsumerState<NewPostFormView> createState() =>
      _newPostFormViewConsumerState();
}

class _newPostFormViewConsumerState extends ConsumerState<NewPostFormView> {
  // State of the Widget
  final TextEditingController userPostTextEditingController =
      TextEditingController();

  @override
  Widget build(BuildContext context) {
    final pb = ref.watch(pocketbaseProvider);
    return Scaffold(
      appBar: AppBar(title: const Text('New Post')),
      body: Center(
        child: Container(
            child: Padding(
              padding: const EdgeInsets.all(16.0),
              child: Column(
                children: [
                  TextFormField(
                    controller: userPostTextEditingController,
                    maxLines: 10,
                  ),
                  SizedBox(height: 30),
                  Row(mainAxisAlignment: MainAxisAlignment.center, children: [
                    ElevatedButton(
                        child: Text("Submit Post"),
                        onPressed: () {
                          pb.collection('posts').create(body: {
                            'post_content': userPostTextEditingController.text,
                            'posted_by': pb.authStore.record!.id,
                            'likes': 1
                          }).then((value) => widget.setStateCallback);
                          Navigator.of(context).pop();
                        }),
                  ])
                ],
              ),
            ),
            height: MediaQuery.of(context).size.height / 3,
            width: MediaQuery.of(context).size.width / 3,
            color: Colors.grey),
      ),
    );
  }
}

I'm building a social feed. I have a view for the list of posts, and a view for creating a post. When the user creates a post, I want to submit it to my backend, make a callback to my setState callback in the views where posts are listed, and then pop the new post view off the widget tree with the result being that the post list view has been rebuilt, and in doing do, rebuilt the future builder getting new posts from the backend. For some reason, That's not working and I'm not sure why. I've even tried breaking the riverpod invocation into it's own function in my list posts class so that something would have to be explicitly invoked but it's not working.

social_feed.dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:irc/features/social/views/social_post_create.dart';

//Import Providers
import 'package:irc/shared/pocketbase_provider.dart';

//Import Feature Widgets
import 'package:irc/features/social/widgets/social_tile.dart';

class SocialFeedScaffold extends ConsumerStatefulWidget {
  const SocialFeedScaffold({super.key});
  @override
  ConsumerState<SocialFeedScaffold> createState() =>
      _SocialFeedScaffoldConsumerState();
}

class _SocialFeedScaffoldConsumerState
    extends ConsumerState<SocialFeedScaffold> {
  void setStateCallback() => setState(() {});

  @override
  Widget build(BuildContext context) {
    final pocketbase = ref.watch(pocketbaseProvider);
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: SingleChildScrollView(
            child: Column(
          children: [
            Padding(
              padding: const EdgeInsets.all(16.0),
              child: Text("What other researchers are talking about...",
                  style: GoogleFonts.cardo(
                      fontSize: 32, fontWeight: FontWeight.bold)),
            ),
            // INLINE SOCIAL FEED
            FutureBuilder(
                future: pocketbase.collection('posts').getList(
                    perPage: 100, sort: '-created', expand: 'posted_by'),
                builder: (BuildContext context, AsyncSnapshot snapshot) {
                  if (snapshot.hasData) {
                    List postList = snapshot.data!.toJson()['items'];
                    return ListView.separated(
                        shrinkWrap: true,
                        itemCount: postList.length,
                        separatorBuilder: (context, index) =>
                            const SizedBox(height: 16),
                        itemBuilder: (BuildContext context, int index) {
                          return SocialTile(
                              postContent: postList[index]['post_content'],
                              postedBy: snapshot.data!.toJson()['items'][index]
                                  ['expand']['posted_by']['username']);
                        });
                  } else if (snapshot.connectionState ==
                      ConnectionState.waiting) {
                    return const CircularProgressIndicator();
                  } else if (snapshot.hasError) {
                    return Text(snapshot.error.toString());
                  } else {
                    return const CircularProgressIndicator();
                  }
                }),
          ],
        )),
      ),
      floatingActionButton: FloatingActionButton(
          backgroundColor: Colors.black,
          child: const Icon(Icons.post_add, color: Colors.white),
          onPressed: () {
            Navigator.of(context).push(MaterialPageRoute(
                builder: (context) => NewPostFormView(
                      setStateCallback: setStateCallback,
                    )));
          }),
    );
  }
}

social_post_create.dart

import 'package:flutter/material.dart';
// You're not importing FLUTTER RIVERPOD, just riverpod and that's fricking your code.
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:irc/shared/pocketbase_provider.dart';
import 'package:pocketbase/pocketbase.dart';
import 'dart:ui';

class NewPostFormView extends ConsumerStatefulWidget {
  // Class Constructor
  const NewPostFormView({super.key, required this.setStateCallback});

  // Class Fields
  final void setStateCallback;
  @override
  ConsumerState<NewPostFormView> createState() =>
      _newPostFormViewConsumerState();
}

class _newPostFormViewConsumerState extends ConsumerState<NewPostFormView> {
  // State of the Widget
  final TextEditingController userPostTextEditingController =
      TextEditingController();

  @override
  Widget build(BuildContext context) {
    final pb = ref.watch(pocketbaseProvider);
    return Scaffold(
      appBar: AppBar(title: const Text('New Post')),
      body: Center(
        child: Container(
            child: Padding(
              padding: const EdgeInsets.all(16.0),
              child: Column(
                children: [
                  TextFormField(
                    controller: userPostTextEditingController,
                    maxLines: 10,
                  ),
                  SizedBox(height: 30),
                  Row(mainAxisAlignment: MainAxisAlignment.center, children: [
                    ElevatedButton(
                        child: Text("Submit Post"),
                        onPressed: () {
                          pb.collection('posts').create(body: {
                            'post_content': userPostTextEditingController.text,
                            'posted_by': pb.authStore.record!.id,
                            'likes': 1
                          }).then((value) => widget.setStateCallback);
                          Navigator.of(context).pop();
                        }),
                  ])
                ],
              ),
            ),
            height: MediaQuery.of(context).size.height / 3,
            width: MediaQuery.of(context).size.width / 3,
            color: Colors.grey),
      ),
    );
  }
}

Share Improve this question asked Feb 4 at 17:49 DJ KDJ K 13 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

You are using riverpod, thus you should not use setState(). Instead of that, you should refresh the page using riverpod, just make:

ref.refresh(pocketbaseProvider);

And the page will be refreshed reloading all data.

Anyway I think you should not make that approach. First of all write down a new provider that fetches data from remote like this:

    @riverpod
    class PocketBaseProvider extends _$PocketBaseProvider{
      @override
      Future<List<Post>> build() => _fetch();
    
      Future<List<Post>> _fetch() async {
        state = const AsyncValue.loading();
        final service = ref.watch(baasSDKProvider);

         state = await AsyncValue.guard(
            () async => service.findAll()   );
    return state.value!;
    
  }
}

(take care of exceptions)

Then your widget should be like this:

class SocialFeedScaffold extends HookConsumerWidget {
  const SocialFeedScaffold ({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final myLocalStateToHandle = useState(false);
    final state = ref.watch(pocketBaseProvider);
    return switch (state) {
      AsyncError<bool>(:final error) => Center(
          child: Text(error.toString()),
        ),
      AsyncData<bool>(:final value) =>
        _buildWithData(context,ref, value, myLocalStateToHandle),
      _ => const Center(
          child: CircularProgressIndicator(),
        ),
    };
  }

Then in the method _buildWithData() you will build your interface using the data from the provider:

 Widget _buildWithData(BuildContext context, WidgetRef ref,
   List<Post>   value, bool myLocalStateToHandle) {
   .....
   if ( myBool ) {
       myLocalStateToHandle.value = .... ; //this would refresh the page
   }

  }

Now, if you want to re-read all data from baasSDKProvider , you can make ref.refresh(baasSDKProvider) in a callback (always in a callback! never in the build() method) of your interface, for example in a simple button for refreshing.

本文标签: