Контроллеры getx вызываются неправильноAndroid

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Контроллеры getx вызываются неправильно

Сообщение Anonymous »

Я создаю новостное приложение на Flutter, используя Getx в качестве управления состояниями.
Flow
if уже есть учетная запись
LoginSreen-> MainScreen(FeedNewsScreen)
если новый пользователь
SingupScreen ->interestScreen -> LoginSreen -> MainScreen(FeedNewsScreen)
ПРОБЛЕМЫ
  • когда бы я ни был запуск приложения, его создание и инициализация Authcontroller, затем создание контроллера FeedNewsController (который я не вызывал), затем exploreNewsController (снова не вызывается), затем BottomNavigationController
  • После Была создана новая учетная запись, и я вхожу в свою учетную запись, и она показывает, что интерес не найден, но список интересов заполнен, но интересы, поскольку методы fetchedIntersts вызываются из метода входа в authcontroller.
3 при использовании Get.put(feednewscontroller) на главном экране после перехода с экрана входа в систему или открытия приложения, если вы уже вошли в систему, я думаю, что инициализация FeedNewsController не работает, так как это отвечает за получение новостей
это мой основной файл

Код: Выделить всё


Future 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 interests = [].obs;
RxInt maxInterest = 0.obs;
RxBool isPressedSignin = false.obs;
// FeedNewsController feedNewsController = Get.find();

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

Future 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 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 logOut() async {
await auth.signOut();
pref.removeUser();
interests.clear();
}

Future  _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 fetchInterests() async {
User? currentUser = auth.currentUser;
if (currentUser != null) {
DocumentSnapshot snapshot =
await firestore.collection('users').doc(currentUser.uid).get();
Map? userData = snapshot.data();
if (userData != null && userData['interests'] != null) {
interests.assignAll(List.from(userData['interests']));
print(interests);
return true;
}
}
return false;
}

Future 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 everyThingNews = [].obs;
RxBool isLoading = false.obs;
RxBool isLoadingMore = false.obs;
RxList sortBy = ['relevancy', 'popularity', 'publishedAt'].obs;
RxString currentSort = 'relevancy'.obs;
RxString errorMessage = ''.obs;

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

Future fetchEveryThingNews() async {
print('everything news called');
try {
isLoading(true);
List interests = authController.interests;
print(interests);
// List 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 fetchMoreNews() async {
try {
isLoadingMore(true);
page.value++;
List 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 _removeDuplicates(List  articles) {
final uniqueArticles = {};
final filteredArticles = [];

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

return filteredArticles;
}
}

это Экран регистрации

Код: Выделить всё


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 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();
// 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(
value: newsController.currentSort.value,
items: newsController.sortBy.map((String value) {
return DropdownMenuItem(
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);
}
},
),
),
],
),
);
}
}),
),
);
}
}

Я хочу, чтобы
  • когда я вхожу в систему или открываю приложение, новости ленты должны получать извлекается и отображается на экране, или список интересов заполняется, если это вызывает проблему.
  • Контроллеры не должны инициализироваться без необходимости.


Подробнее здесь: https://stackoverflow.com/questions/790 ... d-properly
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»