Anonymous
Контроллеры getx вызываются неправильно
Сообщение
Anonymous » 01 окт 2024, 00:14
Я создаю новостное приложение на 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
1727730876
Anonymous
Я создаю новостное приложение на Flutter, используя Getx в качестве управления состояниями. [b]Flow[/b] if уже есть учетная запись LoginSreen-> MainScreen(FeedNewsScreen) если новый пользователь SingupScreen ->interestScreen -> LoginSreen -> MainScreen(FeedNewsScreen) [b]ПРОБЛЕМЫ[/b] [list] [*]когда бы я ни был запуск приложения, его создание и инициализация Authcontroller, затем создание контроллера FeedNewsController (который я не вызывал), затем exploreNewsController (снова не вызывается), затем BottomNavigationController [*]После Была создана новая учетная запись, и я вхожу в свою учетную запись, и она показывает, что интерес не найден, но список интересов заполнен, но интересы, поскольку методы fetchedIntersts вызываются из метода входа в authcontroller. [/list] 3 при использовании Get.put(feednewscontroller) на главном экране после перехода с экрана входа в систему или открытия приложения, если вы уже вошли в систему, я думаю, что инициализация FeedNewsController не работает, так как это отвечает за получение новостей это мой [b]основной файл[/b] [code] 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(), ); }); } } [/code] это [b]AuthController[/b] [code] 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; } } [/code] это [b]FeedNewsController[/b] [code] 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; } } [/code] это [b]Экран регистрации[/b] [code] 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), ], ), ), ), ); } } [/code] это [b]Экран интересов[/b] (где пользователи выбирают интересы) [code] 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')), ) ], ), ), ); } } [/code] это [b]Экран входа[/b] [code] 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, ), ], ), ), ), ); } } [/code] это [b]Главный экран[/b](содержит экран ленты новостей) [code] 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', ) ], ), ), ); } } [/code] это [b]экран ленты[/b] [code] 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); } }, ), ), ], ), ); } }), ), ); } } [/code] [b]Я хочу, чтобы[/b] [list] [*]когда я вхожу в систему или открываю приложение, новости ленты должны получать извлекается и отображается на экране, или список интересов заполняется, если это вызывает проблему. [*]Контроллеры не должны инициализироваться без необходимости. [/list] Подробнее здесь: [url]https://stackoverflow.com/questions/79041036/getx-controllers-are-not-called-properly[/url]