Flutter keeps firing the api request, 200+ x sometimes and this makes the app really slow. I have tried async memorizer already, but this doesn't work. Do you have any idea how I can prevent multiple api requests with this code?
I'm getting an redscreen also when the page is loading. The error says Null check operator used on a null value.
I don't understand what I'm doing wrong. Tips & ideas are welcome.
class ProfileScreen extends StatefulWidget {
// ignore: deprecated_member_use_from_same_package
final ProfileNavigationEnum profileNavigationEnum;
final String? otherUserId;
final String? profileUrl;
final String? coverUrl;
const ProfileScreen({
Key? key,
this.otherUserId,
this.profileUrl,
this.coverUrl,
this.profileNavigationEnum = ProfileNavigationEnum.FROM_FEED,
}) : super(key: key);
#override
_ProfileScreenState createState() => _ProfileScreenState();
}
class _ProfileScreenState extends State<ProfileScreen>
with SingleTickerProviderStateMixin {
ProfileCubit? _profileCubit;
UserPostCubit? userPostCubit;
UserMediaCubit? userMediaCubit;
UserLikesCubit? userLikesCubit;
TabController? tabController;
Size? size;
#override
void initState() {
_profileCubit = BlocProvider.of<ProfileCubit>(context);
tabController = TabController(length: 3, vsync: this);
userPostCubit = getIt<UserPostCubit>();
userMediaCubit = getIt<UserMediaCubit>();
userLikesCubit = getIt<UserLikesCubit>();
_profileCubit!.profileEntity.listen((event) {
userLikesCubit!.userId = event.id;
userMediaCubit!.userId = event.id;
userPostCubit!.userId = event.id;
});
super.initState();
_profileCubit!
.getUserProfile(widget.otherUserId, widget.coverUrl, widget.profileUrl);
}
double textSizePred = 0.0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: BlocConsumer<ProfileCubit, CommonUIState>(
listener: (context, state) => state.maybeWhen(
orElse: () => null,
success: (state) => ProfileListener.success(state, context),
error: (e) => ProfileListener.error(e!, context),
),
builder: (_, state) {
return state.when(
initial: () => LoadingBar(),
success: (s) => getHomeWidget(),
loading: () => LoadingBar(),
error: (e) => Center(
child: NoDataFoundScreen(
onTapButton: context.router.root.pop,
icon: AppIcons.personOption(
color: AppColors.colorPrimary, size: 40),
title: 'Profile Not found',
message: e!.contains("invalid")
? LocaleKeys
.sorry_we_cannot_find_the_page_you_are_looking_for_if_you_still_ne
.tr()
: e,
buttonText: LocaleKeys.go_back.tr(),
),
),
);
},
),
);
}
Widget getHomeWidget() {
return StreamBuilder<ProfileEntity>(
stream: _profileCubit!.profileEntity,
builder: (context, snapshot) {
return DefaultTabController(
length: 3,
child: SafeArea(
child: NestedScrollView(
headerSliverBuilder: (context, value) {
return [
SliverAppBar(
automaticallyImplyLeading: false,
leading: null,
systemOverlayStyle: SystemUiOverlayStyle.light,
elevation: 0.0,
expandedHeight: calculateHeight(
context: context,
item: snapshot.data!,
) as double?,
floating: true,
pinned: true,
actions: [
IconButton(
icon: const Icon(Icons.edit),
onPressed: () async {
await openMediaPicker(
context,
(media) async {
_profileCubit!.changeProfileEntity(
snapshot.data!
.copyWith(backgroundImage: media),
);
await _profileCubit!
.updateProfileCover(media);
},
mediaType: MediaTypeEnum.IMAGE,
allowCropping: true,
);
}).toVisibility(widget.otherUserId == null)
],
// title: Text('Profile'),
backgroundColor: Colors.white,
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.pin,
background: snapshot.data == null
? Container()
: TopAppBar(
otherUserId: snapshot.data!.id,
otherUser: widget.otherUserId != null,
profileEntity: snapshot.data,
profileNavigationEnum:
widget.profileNavigationEnum,
onSizeAborted: (size) {
setState(() {
textSizePred = size;
});
},
),
),
// posts,media,likes row
bottom: PreferredSize(
child: Stack(
children: [
Positioned.fill(
child: Container(
decoration: const BoxDecoration(
color: Colors.white,
border: Border(
bottom: BorderSide(
color: Colors.grey,
width: 0.2,
),
),
),
),
),
TabBar(
indicatorWeight: 1,
indicatorSize: TabBarIndicatorSize.label,
labelPadding: const EdgeInsets.all(0),
labelStyle: TextStyle(
fontFamily: 'CeraPro',
fontWeight: FontWeight.w500,
),
unselectedLabelStyle: TextStyle(
fontFamily: 'CeraPro',
fontWeight: FontWeight.bold,
),
tabs: [
Tab(
text: LocaleKeys.posts.tr(),
).toContainer(
alignment: Alignment.center,
decoration: const BoxDecoration(
color: Colors.white,
border: Border(
top: BorderSide(
color: Colors.grey,
width: 0.2,
),
),
),
),
Tab(
text: LocaleKeys.media.tr(),
).toContainer(
alignment: Alignment.center,
decoration: const BoxDecoration(
color: Colors.white,
border: Border(
top: BorderSide(
color: Colors.grey,
width: 0.2,
),
),
),
),
Tab(
text: LocaleKeys.likes.tr(),
).toContainer(
alignment: Alignment.center,
decoration: const BoxDecoration(
color: Colors.white,
border: Border(
top: BorderSide(
color: Colors.grey,
width: 0.2,
),
),
),
),
],
)
],
),
preferredSize: const Size(500, 56),
),
),
];
},
body: TabBarView(
children: [
/// Add the block widget here thhree times
Container(
child: RefreshIndicator(
onRefresh: () {
userPostCubit!.onRefresh();
return Future.value();
},
child: PostPaginationWidget(
isComeHome: false,
isFromProfileSearch: true,
isPrivateAccount: (value) {
_profileCubit!.isPrivateUser = value;
},
isSliverList: false,
noDataFoundScreen: NoDataFoundScreen(
buttonText: LocaleKeys.go_to_the_homepage.tr(),
title: LocaleKeys.no_posts_yet.tr(),
message: "",
onTapButton: () {
context.router.root.pop();
},
),
pagingController: userPostCubit!.pagingController,
onTapLike: userPostCubit!.likeUnlikePost,
onOptionItemTap:
(PostOptionsEnum postOptionsEnum, int index) =>
userPostCubit!.onOptionItemSelected(
context, postOptionsEnum, index),
onTapRepost: userPostCubit!.repost,
),
),
),
Container(
child: RefreshIndicator(
onRefresh: () {
userMediaCubit!.onRefresh();
return Future.value();
},
child: PostPaginationWidget(
isComeHome: false,
isPrivateAccount: (value) {
_profileCubit!.isPrivateUser = value;
},
isSliverList: false,
noDataFoundScreen: NoDataFoundScreen(
title: LocaleKeys.no_media_yet.tr(),
icon: AppIcons.imageIcon(height: 35, width: 35),
buttonText: LocaleKeys.go_to_the_homepage.tr(),
message: "",
onTapButton: () {
context.router.root.pop();
// BlocProvider.of<FeedCubit>(context).changeCurrentPage(ScreenType.home());
// context.router.root.push(Routes.createPost);
},
),
pagingController: userMediaCubit!.pagingController,
onTapLike: userMediaCubit!.likeUnlikePost,
onOptionItemTap: (PostOptionsEnum postOptionsEnum,
int index) async =>
await userMediaCubit!.onOptionItemSelected(
context, postOptionsEnum, index),
onTapRepost: userMediaCubit!.repost,
),
)),
Container(
child: RefreshIndicator(
onRefresh: () {
userLikesCubit!.onRefresh();
return Future.value();
},
child: PostPaginationWidget(
isComeHome: false,
isPrivateAccount: (value) {
_profileCubit!.isPrivateUser = value;
},
isSliverList: false,
noDataFoundScreen: NoDataFoundScreen(
title: LocaleKeys.no_likes_yet.tr(),
icon: AppIcons.likeOption(
size: 35, color: AppColors.colorPrimary),
buttonText: LocaleKeys.go_to_the_homepage.tr(),
message: LocaleKeys
.you_don_t_have_any_favorite_posts_yet_all_posts_that_you_like_wil
.tr(),
onTapButton: () {
context.router.root.pop();
// BlocProvider.of<FeedCubit>(context).changeCurrentPage(ScreenType.home());
// context.router.root.push(Routes.createPost);
},
),
pagingController: userLikesCubit!.pagingController,
onTapLike: userLikesCubit!.likeUnlikePost,
onOptionItemTap:
(PostOptionsEnum postOptionsEnum, int index) =>
userLikesCubit!.onOptionItemSelected(
context,
postOptionsEnum,
index,
),
onTapRepost: userLikesCubit!.repost,
),
)),
],
),
),
),
);
});
}
num calculateHeight({
required BuildContext context,
required ProfileEntity item,
}) {
print('INCHES: ${context.diagonalInches}');
bool isSmallInches = context.diagonalInches <= 4.7;
bool hasWebsite = item.website != null && item.website!.isNotEmpty;
final height = context.getScreenHeight;
final defaultHeight = isSmallInches ? height * .6 : height * .47;
final websiteHeight = hasWebsite ? height * .03 : 0;
final sizeBoxHeight = textSizePred != 0.0 ? 10.h : 0;
return textSizePred + defaultHeight + websiteHeight + sizeBoxHeight;
}
}
/// helps to determine from where user navigated to profile
/// so that on back press of the profile screen we can go back the correct page
/// we're using this because according to the UI we will have the keep the bottom navigation bar under the profile page
enum ProfileNavigationEnum {
FROM_BOOKMARKS,
FROM_FEED,
FROM_SEARCH,
FROM_VIEW_POST,
FROM_MY_PROFILE,
FROM_OTHER_PROFILE,
FROM_MESSAGES,
FROM_NOTIFICATION
} ```
Related
This bounty has ended. Answers to this question are eligible for a +50 reputation bounty. Bounty grace period ends in 11 hours.
KJEjava48 is looking for a canonical answer.
In my flutter application screen I have a DropdownButtonFormField2 which list various financial years, where after fetching the financial year list I set the current financial year as the initial(default) selected value in the dropdown as code below.The Problem comes when I change the financial year to a different year in dropdown, and it will change to the newly selected financial year in dropdown, but it will reset to the current(initial/default) financial year when I rotate the screen. How to solve this issue?
class AccountSetup extends StatelessWidget {
FinancialYear selFinancialPeriod = FinancialYear();
dynamic selFinancialValue;
List<dynamic>? financialYearList;
final Company selectedCompany;
AccountSetup({Key? key, required this.selectedCompany}) : super(key: key);
#override
Widget build(BuildContext context) {
context.read<MyAccountBloc>().add(FetchFinancialYearList(selectedCompany.id!));
return BlocBuilder<MyAccountBloc, MyAccountState>(
builder: (context, state) {
if(state is FinancialYearList) {
financialYearList = state.list;
if(financialYearList != null) {
for(dynamic itemFY in financialYearList!) {
if(DateTime.now().isBetween(yMdFormat.parse(itemFY['startDate']), yMdFormat.parse(itemFY['endDate']))) {
selFinancialPeriod = FinancialYear.fromJson(itemFY);
selFinancialValue = itemFY;
break;
}
}
}
getFinancialPeriodDropDown(context);
} else if(state is AccountTabChanged) {
....
} else if(state is UpdateDropDown) {
selFinancialValue = state.selValue;
selFinancialPeriod = FinancialYear.fromJson(selFinancialValue);
getFinancialPeriodDropDown(context);
}
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: Stack(
children: [
Container(
child: Column(
children: [
SizedBox(height: 3,),
getFinancialPeriodDropDown(context),
SizedBox(height: 3,),
DefaultTabController(
length: 2, // length of tabs
initialIndex: 0,
child: Builder(
builder: (context) {
tabController = DefaultTabController.of(context);
tabController?.addListener(() {
selTab = tabController?.index;
context.read<MyAccountBloc>().add(ChangeTabEvent(tabIndex: tabController?.index));
});
return Column(
children: <Widget>[
....
],
);
},
),
),
],
),
),
Positioned(
bottom: 0,
left: 30,
right: 30,
child: getTabButtons(context),
),
],
)
),
),
);
}
);
}
getFinancialPeriodDropDown(BuildContext context) {
if(financialYearList == null) {
return SizedBox();
}
return setAcademicDropDown(context);
}
setFinancialPeriodDropDown(BuildContext context) {
return SizedBox(
height: 45,
child: DropdownButtonFormField2<dynamic>(
isExpanded: true,
hint: const Text('Select Financial Year',style: TextStyle(fontSize: 14),),
value: selFinancialValue,
items: financialYearList!.map((item) => DropdownMenuItem<dynamic>(
value: item,
child: Text('${dMyFormat.format(yMdFormat.parse(item['startDate']))} :: ${dMyFormat.format(yMdFormat.parse(item['endDate']))}',
style: const TextStyle(fontSize: 14,),
),)).toList(),
validator: (value) {
if (value == null) {
return 'Please select $txtAcaPer.';
}
},
onChanged: (value) {
context.read<MyAccountBloc>().add(UpdateDropDownEvent(selValue: value));
},
onSaved: (value) {},
),
);
}
}
One more thing I need to know is, how can i set the initial(default) value to nothing (ie, like 'Select Financial Year') when I open the page instead of the current financial year??
Edit :
I saw same kind of problem on the below question also
flutter dropdownbutton won't keep answer after scrolling
If you really don't want to lose the selection on orientation change then make the AccountSetup widget as StatefulWidget.
Then your code will be as following
class AccountSetup extends StatefulWidget {
final Company selectedCompany;
AccountSetup({Key? key, required this.selectedCompany}) : super(key: key);
#override
State<AccountSetup> createState() => _AccountSetupState();
}
class _AccountSetupState extends State<AccountSetup> {
FinancialYear selFinancialPeriod = FinancialYear();
dynamic selFinancialValue;
List<dynamic>? financialYearList;
#override
Widget build(BuildContext context) {
context.read<MyAccountBloc>().add(FetchFinancialYearList(widget.selectedCompany.id!));
return BlocBuilder<MyAccountBloc, MyAccountState>(
builder: (context, state) {
if(state is FinancialYearList) {
financialYearList = state.list;
if(financialYearList != null) {
for(dynamic itemFY in financialYearList!) {
if(DateTime.now().isBetween(yMdFormat.parse(itemFY['startDate']), yMdFormat.parse(itemFY['endDate']))) {
selFinancialPeriod = FinancialYear.fromJson(itemFY);
selFinancialValue = itemFY;
break;
}
}
}
getFinancialPeriodDropDown(context);
} else if(state is AccountTabChanged) {
selTab = state.tabIndex;
//tabController!.index = selTab!;
getTabButtons(context);
} else if(state is UpdateDropDown) {
selFinancialValue = state.selValue;
selFinancialPeriod = FinancialYear.fromJson(selFinancialValue);
getFinancialPeriodDropDown(context);
}
return MaterialApp(
scrollBehavior: MyCustomScrollBehavior(),
title: ....,
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSwatch().copyWith(
primary: primaryColor,
),),
home: Scaffold(
resizeToAvoidBottomInset: false,
appBar: sizedAppBar,
body: SafeArea(
child: Stack(
children: [
Container(
child: Column(
children: [
SizedBox(height: 3,),
getFinancialPeriodDropDown(context),
SizedBox(height: 3,),
DefaultTabController(
length: 2, // length of tabs
initialIndex: 0,
child: Builder(
builder: (context) {
tabController = DefaultTabController.of(context);
tabController?.addListener(() {
selTab = tabController?.index;
context.read<MyAccountBloc>().add(ChangeTabEvent(tabIndex: tabController?.index));
});
return Column(
children: <Widget>[
Container(
child: TabBar(
controller: tabController,
labelColor: textWhite1,
unselectedLabelColor: Colors.black,
indicatorWeight: 2,
isScrollable: true,
indicatorSize: TabBarIndicatorSize.tab,
indicatorColor: selColor1,
indicatorPadding: EdgeInsets.only(left: 2.0, right: 2.0,bottom: 3.0),
indicator: BoxDecoration(
borderRadius: BorderRadius.circular(5),color: highlightGrey10,shape: BoxShape.rectangle,
),
labelPadding: EdgeInsets.symmetric (horizontal: 1),
tabs: [
Container(
width: mainTabWidth,
height: mainTabHeight,
decoration: BoxDecoration(color: tab1Color,border: Border.all(color: border1Color,width: 2,style: BorderStyle.solid),borderRadius: BorderRadius.circular(5)),
child: Tab(child:Text(tabAccSt1,textAlign: TextAlign.center,style: TextStyle(fontSize: fontSize,fontWeight: FontWeight.bold,),),),
),
Container(
width: mainTabWidth,
height: mainTabHeight,
decoration: BoxDecoration(color: tab2Color,border: Border.all(color: border1Color,width: 2,style: BorderStyle.solid),borderRadius: BorderRadius.circular(5)),
child: Tab(child:Text(tabAccSt2,textAlign: TextAlign.center,style: TextStyle(fontSize: fontSize,fontWeight: FontWeight.bold,),),),
),
],
),
),
Container(
height: 400, //height of TabBarView
decoration: BoxDecoration(
border: Border(top: BorderSide(color: Colors.grey, width: 0.5))
),
child: TabBarView(
controller: tabController,
children: <Widget>[
Container(
child: Column(
children: [
getCompanySites(),
],
),
),
Container(
child: Center(
child: Text('Display Tab 2', style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold)),
),
),
],
),
),
],
);
},
),
),
],
),
),
Positioned(
bottom: 0,
left: 30,
right: 30,
child: getTabButtons(context),
),
],
)
),
),
);
}
);
}
getFinancialPeriodDropDown(BuildContext context) {
if(financialYearList == null) {
return SizedBox();
}
return setAcademicDropDown(context);
}
setFinancialPeriodDropDown(BuildContext context) {
return SizedBox(
height: 45,
child: DropdownButtonFormField2<dynamic>(
isExpanded: true,
hint: const Text('Select Financial Year',style: TextStyle(fontSize: 14),),
value: selFinancialValue,
icon: const Icon(Icons.arrow_drop_down,color: Colors.black45,),
iconSize: 30,
buttonHeight: 60,
buttonPadding: const EdgeInsets.only(left: 20, right: 10),
dropdownDecoration: BoxDecoration(borderRadius: BorderRadius.circular(15),),
items: financialYearList!.map((item) => DropdownMenuItem<dynamic>(
value: item,
child: Text('${dMyFormat.format(yMdFormat.parse(item['startDate']))} :: ${dMyFormat.format(yMdFormat.parse(item['endDate']))}',
style: const TextStyle(fontSize: 14,),
),)).toList(),
validator: (value) {
if (value == null) {
return 'Please select $txtAcaPer.';
}
},
onChanged: (value) {
context.read<MyAccountBloc>().add(UpdateDropDownEvent(selValue: value));
},
onSaved: (value) {},
),
);
}
}
If you not seeing any changes on UI then, you have to call setState method to refresh the UI.
Keyboard triggers the api calling while closing and opening
I have search bar I didn't pass anything inside the search bar but when I click the search bar The api call will run.
I don't know why that thing was happen I tried several ways but It won't work
Even Inside the keyboard I choose the one hand keyboard that time also api will run
Also tried the Focusnode also.
class PopupNaviagator extends StatefulWidget {
var body;
String leadsName = "";
Map<String, dynamic> listValues;
PopupNaviagator(this.listValues);
var data;
String pageData = "";
double totalPageCount = 0;
int pageIncrementer = 1;
#override
_PopupNavigatorState createState() => _PopupNavigatorState();
}
class _PopupNavigatorState extends State<PopupNaviagator> {
Utils util = new Utils();
bool isSearching = false;
#override
void initState() {
super.initState();
}
String searchingText = "";
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
titleSpacing: 0,
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: util.getColors(1001),
stops: [0.1, 5.0],
),
),
),
title: !isSearching
? Text("Select Search Type")
: TextField(
autofocus: true,
autocorrect: true,
onChanged: (value) {
searchingText = value;
},
cursorColor: Colors.white,
onSubmitted: (value) {
searchingText = value;
campaignAPI(
widget.listValues["colname"].toString(),
widget.pageIncrementer.toString(),
searchingText, // search key
1, // flag
);
},
style: TextStyle(color: Colors.white),
decoration: InputDecoration(
icon: Icon(Icons.search),
hintText: "Search ",
hintStyle: TextStyle(color: Colors.white)),
),
actions: <Widget>[
isSearching
? IconButton(
icon: Icon(Icons.cancel),
onPressed: () {
setState(() {
searchingText = "";
this.isSearching = false;
campaignAPI(
widget.listValues["colname"].toString(),
widget.pageIncrementer.toString(),
searchingText, // search key
1, // flag
);
/* filteredCountries = countries;*/
});
},
)
: IconButton(
icon: Icon(Icons.search),
onPressed: () {
setState(() {
this.isSearching = true;
});
},
)
],
),
body: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(5))),
child: Scaffold(
body: FutureBuilder(
future: campaignAPI(widget.listValues["colname"].toString(), "1",
searchingText, 0),
builder: (context, snapshot) {
if (snapshot.hasData) {
var body;
if (widget.data == null) {
body = snapshot.data;
} else {
body = widget.data;
}
final records = body["list_records"];
widget.pageData = body["PageValues"];
widget.totalPageCount =
(body["TotalRecordCount"] / 5).toDouble();
return Scaffold(
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
if (records.length != 0) ...[
Expanded(
child: ListView.builder(
padding: EdgeInsets.only(
top: 0,
bottom: 0,
),
physics: ClampingScrollPhysics(),
shrinkWrap: true,
itemCount: records.length,
itemBuilder: (context, index) {
Map<String, dynamic> pickerValues =
records[index];
String titleName =
pickerValues["viewcol_value"]
.toString();
return Container(
margin: const EdgeInsets.all(5.00),
decoration: new BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.grey, width: 1.0),
borderRadius: new BorderRadius.all(
Radius.circular(3.0)),
),
child: FlatButton(
padding: EdgeInsets.all(8.0),
onPressed: () {
setState(() {
List<String> strArr = [
titleName,
pickerValues["rec_id"].toString(),
widget.listValues["colname"]
];
Navigator.pop(context, strArr);
});
},
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(titleName.toString()),
Icon(
Icons.check_circle_outlined,
color: Colors.grey,
),
]),
),
);
}),
),
] else ...[
Container(
height: MediaQuery.of(context).size.height / 1.4,
child: Center(
child: Container(
padding: EdgeInsets.all(65),
child: Text(
"No Record Found",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
color: Colors.black),
),
decoration: BoxDecoration(
border: Border.all(
color: Colors.grey,
width: 2,
),
)),
),
),
],
if (records.length != 0) ...[
Container(
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: () {
if (widget.pageIncrementer > 1) {
widget.pageIncrementer--;
campaignAPI(
widget.listValues["colname"]
.toString(),
widget.pageIncrementer.toString(),
searchingText, // search key
1, // flag
);
setState(() {});
} else {
util.showToast("No records to show");
}
},
child: Icon(Icons.arrow_left_rounded,
size: 50, color: Colors.white),
),
Text(
widget.pageData,
style: TextStyle(
fontSize: 15,
color: Colors.white,
fontWeight: FontWeight.bold),
),
InkWell(
onTap: () {
if (widget.totalPageCount > 1 &&
widget.totalPageCount >=
widget.pageIncrementer) {
widget.pageIncrementer++;
campaignAPI(
widget.listValues["colname"]
.toString(),
widget.pageIncrementer.toString(),
searchingText, // search key
1, // flag
);
} else {
util.showToast("No reocrds to show");
}
},
child: Icon(Icons.arrow_right_rounded,
size: 50, color: Colors.white),
),
],
),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: util.getColors(1001),
stops: [0.1, 5.0],
),
),
)
]
],
),
),
);
} else if (snapshot.hasError) {
return Text('${snapshot.error.toString()}');
}
return Center(
child: CircularProgressIndicator(),
);
},
),
),
));
}
campaignAPI(
String field_col, String page_no, String searchValue, int flag) async {
Utils util = new Utils();
SharedPreferences prefs = await SharedPreferences.getInstance();
final accessKey = prefs.getString('access_key');
final companyName = prefs.getString('company_name');
Object requestParam = {
"companyname": companyName,
"access_key": accessKey.toString(),
"field_col": field_col,
"page_no": page_no,
"searchValue": searchValue,
};
print(requestParam.toString());
APIS apis = new APIS();
Uri url = Uri.parse(apis.searchquickcreate);
util.prints(url);
util.prints(requestParam);
final response = await http.post(url, body: requestParam);
if (response.statusCode == 200) {
final body = json.decode(response.body);
print(body);
String status = body["status"];
if (status != "failed") {
if (flag == 1) {
setState(() {
widget.data = body;
});
}
return body;
} else {
util.logOut(context);
return body;
}
} else {
Navigator.pop(context);
util.showToast("Server Error !");
throw Exception("Server Error !");
}
}
}
I'm developing a dynamic menu in Getx and I'm iterating through a list. It wouldn't be a problem if all the menus were the same, the problem is that the Editores menu is not the default as you can see in the image. Is there a way to leverage my code and print the Editores menu to the screen?
Controller
class HomeController extends GetxController {
final HomeRepository repository;
HomeController(this.repository);
final searchDrawerEC = TextEditingController();
//Variáveis para compor o menu
final _selectedIndex = 0.obs;
int get selectedIndex => _selectedIndex.value;
set selectedIndex(int newValue) => _selectedIndex(newValue);
final items = [].obs;
#override
void onInit() {
items.add(
{
'titulo': 'Home',
'icone': const Icon(
Icons.house_rounded,
color: Colors.white,
),
'rota': Routes.home,
},
);
items.add(
{
'titulo': 'Novas Edições',
'icone': Image.asset(AppImages.novasEdicoes),
'rota': Routes.newEditions,
},
);
items.add(
{
'titulo': 'Editores',
'icone': Image.asset(AppImages.editores),
'rota': '/',
},
);
items.add(
{
'titulo': 'Seguindo',
'icone': Image.asset(AppImages.seguinte),
'rota': '/',
},
);
items.add(
{
'titulo': 'Favoritos',
'icone': Image.asset(AppImages.favorite),
'rota': Routes.favoriteds,
},
);
items.add(
{
'titulo': 'Net',
'icone': Image.asset(AppImages.net1),
'rota': '/',
},
);
items.add(
{
'titulo': 'Configurações',
'icone': const Icon(
Icons.settings,
color: Colors.white,
),
'rota': '/',
},
);
items.add(
{
'titulo': 'MMN',
'icone': const Icon(
Icons.person,
color: Colors.white,
),
'rota': '/',
},
);
super.onInit();
}
Page
class DrawerContentWidget extends GetView<HomeController> {
final int? editores;
final bool? showEditores;
// ignore: use_key_in_widget_constructors
const DrawerContentWidget({this.editores, this.showEditores});
#override
Widget build(BuildContext context) {
return Column(
children: [
CardInfoWidget(
showNet: false,
showEdit: true,
color: const Color(0XFF005E6C),
editor: Get.find<AppController>().currentUser,
),
Column(
children: [
Obx(() => SizedBox(
height: 200,
child: Column(
children: [
ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: controller.items.length,
itemBuilder: (_, index) {
final _item = controller.items[index];
return Obx(() => Container(
decoration: (controller.selectedIndex == index)
? const BoxDecoration(
border: Border(
top: BorderSide(width: 3.0, color: Colors.white),
bottom: BorderSide(width: 3.0, color: Colors.white),
),
)
: null,
child: Card(
color: const Color(0XFF007E94),
elevation: 3,
child: ListTile(
title: Text(
_item['titulo'],
style: const TextStyle(color: Colors.white),
),
leading: _item['icone'],
selected: (controller.selectedIndex == index),
onTap: () => Get.toNamed(_item['rota']),
),
),
),
);
},
),
],
),
)
),
],
)
],
);
}
}
Solved, envolve the card in some OBX then use the controller.selectedIndex = index with the route like this.
onTap: () {
controller.selectedIndex = index ;
Get.toNamed(_item['rota']);
}
Using bloc from rxdart: ^0.24.1
I am trying to save object on mysql. The first try the object get saved succefully, the second try, with a new object, it falling on formKey.currentState.save(). I am using GlobalKey<FormState>() in order to validate the form with Stream
My code is
class DetailGamePage extends StatefulWidget {
#override
_DetailGameState createState() => _DetailGameState();
}
class _DetailGameState extends State<DetailGamePage> {
final formKey = GlobalKey<FormState>();
GameBloc gameBloc;
#override
void didChangeDependencies() {
super.didChangeDependencies();
if (gameBloc == null) {
gameBloc = Provider.gameBloc(context);
}
}
#override
Widget build(BuildContext context) {
Game _game = ModalRoute.of(context).settings.arguments;
if (_game == null) {
_game = Game(
color: "#000000",
description: "",
env: "",
isBuyIt: false,
isOnBacklog: false);
}
return Scaffold(
appBar: AppBar(
iconTheme: IconThemeData(color: Colors.black),
backgroundColor: Colors.white,
title: Text(
"Add Game",
style: TextStyle(color: Colors.black),
),
actions: [
FlatButton(
onPressed: () {
if (formKey.currentState.validate()) {
formKey.currentState.save();
Fluttertoast.showToast(msg: "Game saved");
setState(() {
gameBloc.saveOrUpdate(_game, gameBloc.name,
gameBloc.description, "listGame");
});
Navigator.pushReplacementNamed(context, "home");
}
},
child: Text(
(StringUtils.isNullOrEmpty(_game.id)) ? "Add" : "Update",
style: TextStyle(color: HexColor(_game.color), fontSize: 20),
))
],
),
body: Form(
key: formKey,
child: Stack(children: <Widget>[
_createBackground(context, _game),
_createFormGame(context, _game, gameBloc)
]),
));
}
Widget _createBackground(BuildContext context, Game game) {
final size = MediaQuery.of(context).size;
final gradientTop = Container(
height: size.height, //* 0.4,
width: double.infinity,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: <Color>[HexColor(game.color), Colors.white])),
);
final circule = Container(
width: 100.0,
height: 100.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100.0),
color: Color.fromRGBO(255, 255, 255, 0.1)),
);
return Stack(
children: <Widget>[
gradientTop,
Positioned(
child: circule,
top: 90,
left: 50,
),
Positioned(
child: circule,
top: -40,
right: -30,
),
Container(
padding: EdgeInsets.only(top: 80),
child: Column(
children: <Widget>[
SizedBox(
height: 10.0,
width: double.infinity,
),
],
),
)
],
);
}
Widget _createFormGame(BuildContext context, Game game, GameBloc gameBloc) {
final size = MediaQuery.of(context).size;
return SingleChildScrollView(
child: Column(
children: <Widget>[
SafeArea(
child: Container(
height: 80.0,
)),
Container(
width: size.width * 0.85,
padding: EdgeInsets.symmetric(vertical: 50.0),
margin: EdgeInsets.symmetric(vertical: 30.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(5.0),
boxShadow: <BoxShadow>[
BoxShadow(
color: Colors.black26,
blurRadius: 3.0,
offset: Offset(0.0, 5.0),
spreadRadius: 3.0)
]),
child: Column(
children: <Widget>[
Text("Foto", style: TextStyle(fontSize: 20.0)),
SizedBox(
height: 50.0,
),
_createNameImput(gameBloc, game),
_createDescriptionImput(gameBloc, game),
Divider(
height: 30,
color: HexColor(game.color),
indent: 30,
endIndent: 20,
),
_createWasGameImput(gameBloc, game),
Divider(
height: 30,
color: HexColor(game.color),
indent: 30,
endIndent: 20,
),
_createToTheBacklogImput(gameBloc, game),
SizedBox(height: 60),
_createDeleteButton(gameBloc, game),
SizedBox(height: 60),
],
))
],
),
);
}
#override
void dispose() {
gameBloc?.dispose();
super.dispose();
}
Widget _createWasGameImput(GameBloc gameBloc, Game game) {
return StreamBuilder(
builder: (BuildContext context, AsyncSnapshot snapshot) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: SwitchListTile(
activeColor: HexColor(game.color),
title: Text("Do you have it?"),
value: game.isBuyIt,
onChanged: (bool value) {
setState(() {
game.isBuyIt = value;
});
},
secondary: IconButton(
icon: Icon(Icons.shopping_cart),
onPressed: null,
color: HexColor(game.color),
),
));
},
);
}
Widget _createToTheBacklogImput(GameBloc gameBloc, Game game) {
return StreamBuilder(
builder: (BuildContext context, AsyncSnapshot snapshot) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: SwitchListTile(
activeColor: HexColor(game.color),
title: Text("To the backlog?"),
value: game.isOnBacklog,
onChanged: (bool value) {
setState(() {
game.isOnBacklog = true;
});
},
secondary: IconButton(
icon: Icon(Icons.list),
onPressed: null,
color: HexColor(game.color),
),
));
},
);
}
Widget _createNameImput(GameBloc gamebloc, Game game) {
return Column(children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: TextFormField(
textCapitalization: TextCapitalization.sentences,
initialValue: game.name,
onSaved: (value) {
gameBloc.setName(value);
},
keyboardType: TextInputType.text,
decoration: InputDecoration(
labelText: "Name",
icon: Icon(
Icons.games,
color: HexColor(game.color),
)),
),
),
Divider(
height: 30,
color: HexColor(game.color),
indent: 30,
endIndent: 20,
),
]);
}
Widget _createDescriptionImput(GameBloc gameBloc, Game game) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: TextFormField(
textCapitalization: TextCapitalization.sentences,
initialValue: game.description,
onSaved: (value) {
gameBloc.setDescription(value);
},
keyboardType: TextInputType.text,
decoration: InputDecoration(
labelText: "Description",
icon: Icon(
Icons.description,
color: HexColor(game.color),
)),
),
);
}
Widget _createDeleteButton(GameBloc gameBloc, Game game) {
if (StringUtils.isNotNullOrEmpty(game.id)) {
return FlatButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text("Do you wan to remove the game"),
actions: <Widget>[
FlatButton(
onPressed: () {
setState(() {
gameBloc.remove(game, "listGame");
});
Navigator.pop(context);
Navigator.pop(context);
},
child: Text("Yes")),
FlatButton(
onPressed: () => Navigator.of(context).pop(),
child: Text("No"))
],
);
});
},
child: Text("Remove Game"));
} else {
return Container();
}
}
}
This is the bloc
class GameBloc extends Validators {
//Controller
final _allDataGames = BehaviorSubject<List<Game>>();
final _descriptionController = BehaviorSubject<String>();
final _nameController = BehaviorSubject<String>();
final _allMyListGamesByNameController = BehaviorSubject<List<Game>>();
//Services
GameService gameService = GameService();
//get Data from streams
Stream<List<Game>> get allGameData => _allDataGames.stream;
Stream<List<Game>> get allGameByNameList =>
_allMyListGamesByNameController.stream;
Stream<String> get getDescriptionStream =>
_descriptionController.stream.transform(validateDescription);
Stream<String> get getNameStream =>
_nameController.stream.transform(validName);
//Observable
Stream<bool> get validateDescriptionStream =>
Rx.combineLatest([getDescriptionStream], (description) => true);
Stream<bool> get validateNameStream =>
Rx.combineLatest([getNameStream], (name) => true);
//Set Stream
Function(String) get setDescription => _descriptionController.sink.add;
Function(String) get setName => _nameController.sink.add;
//Get Stream
//From repo
void allGames() async {
List<Game> games = await gameService.getAllDataGames();
_allDataGames.sink.add(games);
}
//From my setting
void allMyListGamesByName(String listName) async {
List<Game> games = await gameService.allMyListGamesByName(listName);
_allMyListGamesByNameController.sink.add(games);
}
void saveOrUpdate(
Game game, String name, String description, String listGame) {
game.name = name;
game.description = description;
if (StringUtils.isNullOrEmpty(game.id)) {
game.id = Uuid().v1();
gameService.add(game, listGame);
} else {
gameService.update(game);
}
}
void remove(Game game, String listGame) {
gameService.remove(game, listGame);
}
//Get Lastest stream value
String get name => _nameController.value;
String get description => _descriptionController.value;
dispose() {
_descriptionController?.close();
_allMyListGamesByNameController?.close();
_allDataGames?.close();
_nameController?.close();
}
}
The provider:
class Provider extends InheritedWidget {
static Provider _imstance;
final _gameBloc = GameBloc();
factory Provider({Key key, Widget child}) {
if (_imstance == null) {
_imstance = new Provider._internal(key: key, child: child);
}
return _imstance;
}
Provider._internal({Key key, Widget child}) : super(key: key, child: child);
static GameBloc gameBloc(BuildContext context) {
return (context.inheritFromWidgetOfExactType(Provider) as Provider)
._gameBloc;
}
#override
bool updateShouldNotify(InheritedWidget oldWidget) {
return true;
}
}
The error is:
════════ Exception caught by gesture ═══════════════════════════════════════════
Bad state: Cannot add new events after calling close
When I evaluate formKey.currentState.save(); I got:
formKey.currentState.save()
Unhandled exception:
Bad state: Cannot add new events after calling close
#0 _BroadcastStreamController.add (dart:async/broadcast_stream_controller.dart:249:24)
#1 Subject._add (package:rxdart/src/subjects/subject.dart:141:17)
#2 Subject.add (package:rxdart/src/subjects/subject.dart:135:5)
#3 _StreamSinkWrapper.add (package:rxdart/src/subjects/subject.dart:167:13)
I was reading about this error, it mention the error is on Bloc singleston scope or dispose method.
What is happen?
When you navigate to home with Navigator.pushReplacementNamed(context, "home"), the _DetailGamePage<State> is being disposed, calling gameBloc?.dispose. This leaves _gameBloc instantiated with all streams closed.
As you are using a Singleton Provider, when you navigate back to DetailGamePage, your save is trying to write to the closed streams.
What you need to do is move the closure of the streams farther up the widget tree so as not to close them before you are done with them, perhaps at the app level OR re-instantiate _gameBloc if the streams are closed, loading the data from the repo again.
I have encountered a problem which I cannot move between pages when I click on the icons, but I am setting the default page is the home page and when the application is running it only appears and I cannot move to other pages using the navigation bar.
Navigation Bar File:
class NavigationBar extends StatefulWidget {
#override
_NavigationBarState createState() => _NavigationBarState();
}
class _NavigationBarState extends State<NavigationBar> {
int pageIndex = 0;
final Search _search = Search();
final UserNoti _userNoti = UserNoti();
final HomePage _homePage = HomePage();
final MyProfile _myProfile = MyProfile();
final MyMajor _mymajor = MyMajor();
Widget _showPage = new HomePage();
Widget _pageChooser(int page)
{
switch (page)
{
case 0:
return _search;
break;
case 1:
return _userNoti;
break;
case 2:
return _homePage;
break;
case 3:
return _myProfile;
break;
case 4:
return _mymajor;
break;
default:
return _homePage;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: Container(
decoration: new BoxDecoration(
// borderRadius: BorderRadius.circular(28.0),
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [Colors.deepPurple, Colors.purple]),
boxShadow: [
BoxShadow(
color: Colors.black,
blurRadius: 25.0, // soften the shadow
spreadRadius: -10.0, //extend the shadow
)
],
),
child: CurvedNavigationBar(
index: 2,
color: Color.fromRGBO(58, 66, 86, 1.0),
buttonBackgroundColor: Colors.purple.withOpacity(0.6),
backgroundColor: Colors.transparent,
animationDuration: Duration(milliseconds: 200),
animationCurve: Curves.bounceInOut,
items: <Widget>[
Icon(Icons.search, size: 32, color: Colors.white,),
Icon(Icons.notifications_active, size: 32, color: Colors.white,),
Icon(Icons.home, size: 40, color: Colors.white,),
Icon(Icons.chat, size: 32, color: Colors.white,),
Icon(Icons.school, size: 32, color: Colors.white,),
],
onTap: (int tappedPage){
_showPage = _pageChooser(tappedPage);
},),
),
body: Container(
color: Colors.transparent,
child: Center(
child: _showPage,
),
));
}
}
Now I will put one code for one of those pages for example User Notification Page:
class UserNoti extends StatefulWidget {
final String myuid,majname,uniname;
UserNoti({Key key, this.myuid,this.majname,this.uniname}) : super(key: key);
#override
_UserNotiState createState() => _UserNotiState();
}
class _UserNotiState extends State<UserNoti> {
String myapplang = null;
var my_uid;
var myDoc;
bool loading = false;
#override
void initState() {
super.initState();
checkLang();
}
#override
Widget build(BuildContext context) {
return loading
? LoadingMain()
: Directionality(
textDirection: myapplang == 'ar' ? TextDirection.rtl : TextDirection.ltr,
child: Scaffold(
backgroundColor: Color.fromRGBO(58, 66, 86, 1.0),
appBar: GradientAppBar(
backgroundColorStart: Colors.deepPurple,
backgroundColorEnd: Colors.purple,
title: Text(
myapplang == 'ar' ? "الإشعارات" : "Notifications" ,
style: TextStyle(fontSize: 16,fontWeight: FontWeight.bold,fontFamily: myapplang == 'ar' ? 'Tajawal' : 'BalooDa2' ),),
),
body: ListView(
children: <Widget>[
Column(
children: <Widget>[
SizedBox(height: 30.0,),
Container(
width: MediaQuery.of(context).size.width*0.85,
child: Stack(
children: <Widget>[
StreamBuilder(
stream: Firestore.instance.collection('users').document(myDoc).collection('Notifications').orderBy('date',descending: true).snapshots(),
builder: (context, snapshot){
if(!snapshot.hasData)
{
const Text('Loading...');
}
else{
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index)
{
DocumentSnapshot mypost = snapshot.data.documents[index];
return Stack(
children: <Widget>[
FadeAnimation(0.2,Column(
children: <Widget>[
Container(
decoration: BoxDecoration(borderRadius:
BorderRadius.circular(15.0),color: Color.fromRGBO(64, 75, 96, .9),
boxShadow: [
BoxShadow(
color: Colors.black,
blurRadius: 30.0, // soften the shadow
spreadRadius: -25.0, //extend the shadow
)
],),
child:InkWell(
onTap: () {
// setState(() => loading = true);
if(mypost['type'] == 'comment'){
if(mypost['location'] == 'Majors'){
Firestore.instance
.document('/Majors/${widget.majname}/Posts/${mypost['pdoc']}')
.get()
.then((val) {
if(val.data['pid'] != null && val.data['text'] != null && val.data['uid'] != null && val.data['uimg'] != null && val.data['uname'] != null && val.data['uname'] != null)
{
Navigator.push(context, new MaterialPageRoute(builder: (context) => new Comments(pid: val.data['pid'], text: val.data['text'],img: val.data['img'],uid: val.data['uid'], uimg: val.data['uimg'],uname: val.data['uname'],majname: widget.majname,pdoc:mypost['pdoc'] )));
}
else {
showWraning(context,myapplang == 'ar' ? "حدثت مشكلة، قد يكون الإشعار محذوف أو مشكلة بالإنترنت، حاول لاحقًا." : "There is a problem, maybe notification has been deleted or problem with internet connection, try again later.");
}
/*
*/
}).catchError((e) {
print(e);
});
}
else {
Firestore.instance
.document('/Universities/${widget.uniname}/Posts/${mypost['pdoc']}')
.get()
.then((val) {
if(val.data['pid'] != null && val.data['text'] != null && val.data['uid'] != null && val.data['uimg'] != null && val.data['uname'] != null && val.data['uname'] != null)
{
Navigator.push(context, new MaterialPageRoute(builder: (context) => new U_Comments(pid: val.data['pid'], text: val.data['text'],img: val.data['img'],uid: val.data['uid'], uimg: val.data['uimg'],uname: val.data['uname'],uniname: widget.uniname,pdoc:mypost['pdoc'] )));
}
else {
showWraning(context,myapplang == 'ar' ? "حدثت مشكلة، قد يكون الإشعار محذوف أو مشكلة بالإنترنت، حاول لاحقًا." : "There is a problem, maybe notification has been deleted or problem with internet connection, try again later.");
}
/*
*/
}).catchError((e) {
print(e);
});
}
}
else {
print('not comment!');
}
},
child: ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
leading: ClipRRect(
borderRadius: BorderRadius.circular(35.0),
child: Icon(Icons.insert_comment,color: Colors.deepPurple,size: 40,),
),
title: Column(
children: <Widget>[
Text(
"${mypost['text']}",
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold,fontFamily: 'BalooDa2'),
textAlign: TextAlign.left,
),
SizedBox(height: 10.0,),
],
),
// subtitle: Text("Intermediate", style: TextStyle(color: Colors.white)),
trailing:
Icon(Icons.keyboard_arrow_right, color: Colors.white, size: 30.0)),
),
),
SizedBox(height: 20.0,),
],
),),
],
);
}
);
}
return Container(
height: 0.0,
width: 0.0,
);
},
),
],
),
),
],
),
],
),
),
);
}
I removed a few codes that had no need.
Note when running the application the active page is Homepage()
Right now you are setting your _showPage variable with a new page, but not in a way that will force your app to re-render the widget tree. Try using SetState in your tap function to do this.
onTap: (int tappedPage){
setState(() {
_showPage = _pageChooser(tappedPage);
});
}),