getx 控制器未正确调用

问题描述 投票:0回答:1

我正在使用 Getx 作为状态管理在 flutter 中制作一个新闻应用程序。

流量

如果已有账户 登录屏幕->主屏幕(FeedNewsScreen)

如果是新用户 SingupScreen ->interestScreen ->LoginSreen->MainScreen(FeedNewsScreen)

问题

  1. 当我启动应用程序时,它会创建并初始化Authcontroller,然后创建feednewscontroller(我没有调用)然后exploreNewsController(再次没有调用)然后BottomNavigationController

  2. 创建新帐户并登录我的帐户后,它显示我没有找到兴趣,但已填充兴趣列表,但从 authcontroller 中的登录方法调用兴趣作为 fetchedIntersts 方法。

3 当从登录屏幕导航后在主屏幕中使用 Get.put(feednewscontroller) 或打开应用程序(如果已登录)时,我认为 feednewscontroller 的 init 不起作用,因为它负责获取新闻

这是我的主文件



Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  MyApp({super.key});
  final AuthController authController =
      Get.put(AuthController(), permanent: true);
  final BottomNavigationController navigationController =
      Get.put(BottomNavigationController(), permanent: true);

  @override
  Widget build(BuildContext context) {
    return Obx(() {
      if (navigationController.themeMode.value == null ||
          authController.user == null) {
        return const CircularProgressIndicator();
      }

      return GetMaterialApp(
        debugShowCheckedModeBanner: false,
        title: 'Flutter Demo',
        theme: AppTheme.lightTheme,
        darkTheme: AppTheme.darkTheme,
        themeMode: navigationController.themeMode.value,
        home: authController.isUserLoggedIn() ? MainPage() : WelcomePage(),
      );
    });
  }
}

这是AuthController



class AuthController extends GetxController {
  FirebaseAuth auth = FirebaseAuth.instance;
  FirebaseFirestore firestore = FirebaseFirestore.instance;
  UserPreference pref = UserPreference();
  UserModel user = UserModel();
  RxList<String> interests = <String>[].obs;
  RxInt maxInterest = 0.obs;
  RxBool isPressedSignin = false.obs;
  // FeedNewsController feedNewsController = Get.find();

  @override
  Future<void> onInit() async {
    super.onInit();
    user = await pref.getUser();
    if (user.isLogin == true) {
      await fetchInterests();
    }
  }

  Future<bool> register(String email, String password) async {
    try {
      UserCredential userCredential = await auth.createUserWithEmailAndPassword(
          email: email, password: password);
      await _setUserToken(userCredential);
      return true;
    } catch (e) {
      Get.snackbar("Error", e.toString(), snackPosition: SnackPosition.BOTTOM);
      return false;
    }
  }

  Future<bool> login(String email, String password) async {
    bool interestFetched = false;

    try {
      UserCredential userCredential = await auth.signInWithEmailAndPassword(
          email: email, password: password);
      user = UserModel(
          isLogin: true,
          token: await userCredential.user!.getIdToken(),
          uid: userCredential.user!.uid);
      pref.saveUser(user);
      interestFetched = await fetchInterests();
      // pref.saveInterest(interests);
      // feedNewsController.fetchEveryThingNews();

      print("interest fetched $interestFetched");

      return interestFetched;
    } catch (e) {
      Get.snackbar("Error", e.toString(), snackPosition: SnackPosition.BOTTOM);
      return interestFetched;
    }
  }

  Future<void> logOut() async {
    await auth.signOut();
    pref.removeUser();
    interests.clear();
  }

  Future<void> _setUserToken(UserCredential userCredential) async {
    User? user = userCredential.user;
    if (user != null) {
      String? token = await user.getIdToken();
      await firestore.collection('users').doc(user.uid).set({
        'token': token,
      }, SetOptions(merge: true));
    }
  }

  Future<bool> fetchInterests() async {
    User? currentUser = auth.currentUser;
    if (currentUser != null) {
      DocumentSnapshot<Map<String, dynamic>> snapshot =
          await firestore.collection('users').doc(currentUser.uid).get();
      Map<String, dynamic>? userData = snapshot.data();
      if (userData != null && userData['interests'] != null) {
        interests.assignAll(List<String>.from(userData['interests']));
        print(interests);
        return true;
      }
    }
    return false;
  }

  Future<void> updateInterests() async {
    User? currentUser = auth.currentUser;
    if (currentUser != null) {
      await firestore.collection('users').doc(currentUser.uid).set({
        'interests': interests.toList(),
      });
    }
    print('saved  $interests');
    interests.clear();
  }

  bool isUserLoggedIn() {
    return user.isLogin ?? false;
  }
}

这是FeedNewsController



class FeedNewsController extends GetxController {
  final NewsRepo _newsRepo = NewsRepo();
  UserPreference pref = UserPreference();
  AuthController authController = Get.find();
  RxInt page = 1.obs;
  RxInt pageSize = 10.obs;
  RxList<Articles> everyThingNews = <Articles>[].obs;
  RxBool isLoading = false.obs;
  RxBool isLoadingMore = false.obs;
  RxList<String> sortBy = ['relevancy', 'popularity', 'publishedAt'].obs;
  RxString currentSort = 'relevancy'.obs;
  RxString errorMessage = ''.obs;

  @override
  Future<void> onInit() async {
    super.onInit();
    authController.fetchInterests;
    print('${authController.interests}');
    await fetchEveryThingNews();
  }

  Future<void> fetchEveryThingNews() async {
    print('everything news called');
    try {
      isLoading(true);
      List interests = authController.interests;
      print(interests);
      // List<String> interests = await pref.getInterest();
      if (interests.isEmpty) {
        print("$interests  no interst found");

        errorMessage('no interst found.');
        return;
      }
      String interestsQuery =
          interests.map((interest) => '"$interest"').join(' OR ');
      NewsModel newsModel = await _newsRepo.fetchEverythingNews(
        search: interestsQuery,
        page: page.value.toString(),
        pageSize: pageSize.value.toString(),
        sortBy: currentSort.value,
      );
      everyThingNews.value = _removeDuplicates(newsModel.articles ?? []);
    } catch (e) {
      errorMessage(e.toString());
    } finally {
      isLoading(false);
    }
  }

  Future<void> fetchMoreNews() async {
    try {
      isLoadingMore(true);
      page.value++;
      List<String> interests = authController.interests;
      String interestsQuery =
          interests.map((interest) => '"$interest"').join(' OR ');
      NewsModel newsModel = await _newsRepo.fetchEverythingNews(
        search: interestsQuery,
        page: page.value.toString(),
        pageSize: pageSize.value.toString(),
        sortBy: currentSort.value,
      );
      everyThingNews.addAll(_removeDuplicates(newsModel.articles ?? []));
    } catch (e) {
      errorMessage(e.toString());
    } finally {
      isLoadingMore(false);
    }
  }

  List<Articles> _removeDuplicates(List<Articles> articles) {
    final uniqueArticles = <String>{};
    final filteredArticles = <Articles>[];

    for (var article in articles) {
      if (uniqueArticles.add(article.title!)) {
        filteredArticles.add(article);
      }
    }

    return filteredArticles;
  }
}

这是singup屏幕



class SignUpScreen extends StatelessWidget {
  SignUpScreen({super.key});

  final TextEditingController email = TextEditingController();
  final TextEditingController password = TextEditingController();
  final AuthController authController = Get.find();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SingleChildScrollView(
        child: Padding(
          padding: EdgeInsets.all(AppDimension().defaultMargin),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const SizedBox(
                height: 100,
              ),
              Align(
                  alignment: Alignment.topLeft,
                  child: Text(
                    ' Get the latest news right away',
                    style: Theme.of(context).textTheme.titleMedium,
                  )),
              const SizedBox(
                height: 40,
              ),
              Align(
                alignment: Alignment.centerLeft,
                child: Text(
                  'Email',
                  style: Theme.of(context).textTheme.bodyMedium,
                ),
              ),
              const SizedBox(
                height: 8,
              ),
              TextFormField(
                controller: email,
                keyboardType: TextInputType.emailAddress,
                textInputAction: TextInputAction.next,
                decoration: const InputDecoration(
                  hintText: "email",
                  prefixIcon: Padding(
                    padding: EdgeInsets.all(8),
                    child: Icon(Icons.person),
                  ),
                ),
              ),
              const SizedBox(
                height: 24,
              ),
              Align(
                alignment: Alignment.centerLeft,
                child: Text(
                  'Password',
                  style: Theme.of(context).textTheme.bodyMedium,
                ),
              ),
              const SizedBox(
                height: 8,
              ),
              TextFormField(
                controller: password,
                textInputAction: TextInputAction.done,
                obscureText: true,
                decoration: const InputDecoration(
                  hintText: "password",
                  prefixIcon: Padding(
                    padding: EdgeInsets.all(8),
                    child: Icon(Icons.lock),
                  ),
                ),
              ),
              const SizedBox(height: 16),
              Obx(
                () => ElevatedButton(
                  onPressed: () async {
                    String xemail = email.text.trim();
                    String xpassword = password.text.trim();
                    if (xemail.isEmpty || xpassword.isEmpty) {
                      print('empty fields;');
                    } else {
                      authController.isPressedSignin.value = true;
                      print(authController.isPressedSignin.value);
                      bool isSigned =
                          await authController.register(xemail, xpassword);
                      if (isSigned) {
                        Navigator.push(
                          context,
                          MaterialPageRoute(
                            builder: (context) => InterestsScreen(),
                          ),
                        );
                      }
                    }
                  },
                  style: ButtonStyle(
                      backgroundColor:
                          const WidgetStatePropertyAll(AppColor.primary),
                      shape: WidgetStatePropertyAll(RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(4)))),
                  child: authController.isPressedSignin.value
                      ? const CircularProgressIndicator()
                      : Text(
                          "sign up".toUpperCase(),
                        ),
                ),
              ),
              const SizedBox(height: 8),
            ],
          ),
        ),
      ),
    );
  }
}

这是兴趣屏幕(用户将在其中选择兴趣)




class InterestsScreen extends StatelessWidget {
  InterestsScreen({super.key});
  AuthController authController = Get.find();
  @override
  Widget build(BuildContext context) {
    return Obx(
      () => Padding(
        padding: EdgeInsets.symmetric(vertical: AppDimension().defaultMargin),
        child: Column(
          children: [
            const SizedBox(
              height: 24,
            ),
            Expanded(
              child: Container(
                child: SingleChildScrollView(
                  child: Wrap(
                    alignment: WrapAlignment.center,
                    children: AppString.newsTopics
                        .map(
                          (e) => Padding(
                            padding:
                                const EdgeInsets.symmetric(horizontal: 4.0),
                            child: FilledButton(
                              onPressed: () {
                                if (authController.interests.contains(e)) {
                                  authController.interests.remove(e);

                                  authController.maxInterest--;
                                  print(
                                      "removed   $authController.maxInterest");
                                } else {
                                  if (authController.maxInterest.value >= 5) {
                                    Get.snackbar("Limit reached",
                                        "You can choose only 5 interests",
                                        snackPosition: SnackPosition.BOTTOM);
                                  } else {
                                    authController.interests.add(e);
                                    authController.maxInterest++;
                                    print(
                                        "added   $authController.maxInterest");
                                  }
                                }
                              },
                              style: ButtonStyle(
                                  shape: WidgetStatePropertyAll(
                                      RoundedRectangleBorder(
                                          borderRadius:
                                              BorderRadius.circular(20),
                                          side: const BorderSide(
                                              color: AppColor.light))),
                                  backgroundColor: WidgetStatePropertyAll(
                                      authController.interests.contains(e)
                                          ? AppColor.primary
                                          : Colors.transparent)),
                              child: Text(
                                e,
                                style: const TextStyle(color: AppColor.light),
                              ),
                            ),
                          ),
                        )
                        .toList(),
                  ),
                ),
              ),
            ),
            const SizedBox(
              height: 16,
            ),
            Container(
              margin: EdgeInsets.symmetric(
                  horizontal: AppDimension().defaultMargin),
              width: double.infinity,
              child: ElevatedButton(
                  style: ButtonStyle(
                      backgroundColor:
                          const WidgetStatePropertyAll(AppColor.primary),
                      shape: WidgetStatePropertyAll(RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(4)))),
                  onPressed: () {
                    if (authController.interests.isEmpty) {
                      Get.snackbar("No Interests Selected",
                          "Please select at least one interest.",
                          snackPosition: SnackPosition.BOTTOM);
                    } else {
                      authController.updateInterests();
                      Navigator.pushReplacement(
                        context,
                        MaterialPageRoute(
                          builder: (context) => LoginScreen(),
                        ),
                      );
                    }
                  },
                  child: const Text('Done')),
            )
          ],
        ),
      ),
    );
  }
}

这是登录屏幕



class LoginScreen extends StatelessWidget {
  LoginScreen({super.key});

  TextEditingController email = TextEditingController();
  TextEditingController password = TextEditingController();
  AuthController authController = Get.find();
  BottomNavigationController navigationController = Get.find();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SingleChildScrollView(
        child: Padding(
          padding: EdgeInsets.all(AppDimension().defaultMargin),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              const SizedBox(
                height: 100,
              ),
              Align(
                alignment: Alignment.topLeft,
                child: Text(
                  'Lets LogIn',
                  style: Theme.of(context).textTheme.titleMedium,
                ),
              ),
              const SizedBox(
                height: 40,
              ),
              Align(
                alignment: Alignment.centerLeft,
                child: Text(
                  'Email',
                  style: Theme.of(context).textTheme.bodyMedium,
                ),
              ),
              const SizedBox(
                height: 8,
              ),
              TextFormField(
                controller: email,
                keyboardType: TextInputType.emailAddress,
                textInputAction: TextInputAction.next,
                onSaved: (email) {},
                decoration: const InputDecoration(
                  hintText: "email",
                  prefixIcon: Padding(
                    padding: EdgeInsets.all(8),
                    child: Icon(Icons.person),
                  ),
                ),
              ),
              const SizedBox(
                height: 24,
              ),
              Align(
                alignment: Alignment.centerLeft,
                child: Text(
                  'Password',
                  style: Theme.of(context).textTheme.bodyMedium,
                ),
              ),
              const SizedBox(
                height: 8,
              ),
              TextFormField(
                controller: password,
                textInputAction: TextInputAction.done,
                obscureText: true,
                decoration: const InputDecoration(
                  hintText: "password",
                  prefixIcon: Padding(
                    padding: EdgeInsets.all(8),
                    child: Icon(Icons.lock),
                  ),
                ),
              ),
              const SizedBox(
                height: 24,
              ),
              ElevatedButton(
                onPressed: () async {
                  String xemail = email.text.trim();
                  String xpassword = password.text.trim();
                  bool isLoggedIn =
                      await authController.login(xemail, xpassword);
                  if (isLoggedIn) {
                    navigationController.currentIndex.value = 0;

                    Navigator.pushAndRemoveUntil(
                      context,
                      MaterialPageRoute(builder: (context) => MainPage()),
                      (Route<dynamic> route) => false,
                    );
                  }
                },
                style: ButtonStyle(
                  backgroundColor:
                      const WidgetStatePropertyAll(AppColor.primary),
                  shape: WidgetStatePropertyAll(RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(4))),
                ),
                child: Text(
                  "Login".toUpperCase(),
                ),
              ),
              const SizedBox(
                height: 8,
              ),
            ],
          ),
        ),
      ),
    );
  }
}

这是主屏幕(包含提要屏幕)



class MainPage extends StatelessWidget {
  MainPage({super.key});

  @override
  Widget build(BuildContext context) {
    print('main screen called');
    final BottomNavigationController navigationController =
        Get.find<BottomNavigationController>();
    // Get.lazyPut(() => FeedNewsController(), fenix: true);

    return Scaffold(
      body: Obx(() =>
          navigationController.pages[navigationController.currentIndex.value]),
      bottomNavigationBar: Obx(
        () => BottomNavigationBar(
          onTap: (value) => navigationController.onTabTapped(value),
          currentIndex: navigationController.currentIndex.value,
          items: const [
            BottomNavigationBarItem(
              icon: Icon(Icons.grid_view_outlined),
              label: 'Feed',
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.explore_outlined),
              label: 'Explore',
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.person_4_outlined),
              label: 'Profile',
            )
          ],
        ),
      ),
    );
  }
}

这是饲料屏幕


class FeedScreen extends StatelessWidget {
  final FeedNewsController newsController = Get.find();
  final AppDimension dimension = AppDimension();
  final ScrollController _scrollController = ScrollController();

  FeedScreen({super.key}) {
    _scrollController.addListener(_onScroll);
  }

  void _onScroll() {
    if (_scrollController.position.atEdge) {
      bool isBottom = _scrollController.position.pixels != 0;
      if (isBottom) {
        newsController.fetchMoreNews();
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        appBar: AppBar(
          elevation: 5,
          title: SizedBox(height: 20, child: Image.asset('assets/logo.png')),
        ),
        body: Obx(() {
          if (newsController.isLoading.value) {
            return const Center(child: AppWidgets.loadingIndicator);
          } else if (newsController.errorMessage.isNotEmpty) {
            return Center(child: Text('Error: ${newsController.errorMessage}'));
          } else if (newsController.everyThingNews.isEmpty) {
            return const Center(child: Text('No news articles available.'));
          } else {
            return SingleChildScrollView(
              controller: _scrollController,
              child: Column(
                children: [
                  const SizedBox(height: 21),
                  Padding(
                    padding: EdgeInsets.all(dimension.defaultMargin)
                        .copyWith(bottom: 0, top: 0),
                    child: Row(
                      children: [
                        Text('My Feed',
                            style: Theme.of(context).textTheme.titleLarge),
                        const Spacer(),
                        const Icon(Icons.search),
                      ],
                    ),
                  ),
                  const SizedBox(height: 16),
                  CarouselSlider.builder(
                    itemCount: 5,
                    itemBuilder: (context, index, realIndex) {
                      return NewsCard(
                          news: newsController.everyThingNews[index]);
                    },
                    options: CarouselOptions(
                      height: 235,
                      viewportFraction: .8,
                      enlargeStrategy: CenterPageEnlargeStrategy.scale,
                      enableInfiniteScroll: false,
                      initialPage: 2,
                      autoPlay: true,
                    ),
                  ),
                  const SizedBox(height: 12),
                  Padding(
                    padding: EdgeInsets.all(dimension.defaultMargin)
                        .copyWith(bottom: 0, top: 0),
                    child: Row(
                      children: [
                        DropdownButton<String>(
                          value: newsController.currentSort.value,
                          items: newsController.sortBy.map((String value) {
                            return DropdownMenuItem<String>(
                              value: value,
                              child: Text(value,
                                  style: Theme.of(context)
                                      .textTheme
                                      .headlineMedium),
                            );
                          }).toList(),
                          onChanged: (newValue) {
                            newsController.currentSort.value = newValue!;
                            newsController.page.value = 1;
                            newsController.fetchEveryThingNews();
                          },
                        )
                      ],
                    ),
                  ),
                  const SizedBox(height: 16),
                  Padding(
                    padding: EdgeInsets.all(dimension.defaultMargin)
                        .copyWith(bottom: 0, top: 0),
                    child: ListView.builder(
                      physics: const NeverScrollableScrollPhysics(),
                      shrinkWrap: true,
                      itemCount: newsController.everyThingNews.length +
                          (newsController.isLoadingMore.value ? 1 : 0),
                      itemBuilder: (context, index) {
                        if (index < newsController.everyThingNews.length) {
                          final newsArticle =
                              newsController.everyThingNews[index + 5];
                          return Column(
                            children: [
                              NewsTile(news: newsArticle),
                              const Divider(height: 32),
                            ],
                          );
                        } else {
                          return const Center(
                              child: AppWidgets.loadingIndicator);
                        }
                      },
                    ),
                  ),
                ],
              ),
            );
          }
        }),
      ),
    );
  }
}

我想要

  1. 当我登录或打开应用程序时,应该获取提要新闻并显示在屏幕上,或者如果引起问题,则填充兴趣列表
  2. 控制器不应进行不必要的初始化。
android flutter firebase dart flutter-getx
1个回答
0
投票

我认为BottomNavigationController中的问题你需要提供来发布BottomNavigationController的代码

© www.soinside.com 2019 - 2024. All rights reserved.