GetX update a widget with list of widgets - flutter

So what I want to do is pretty simple I have an integer called currentStep and I also have a list of widgets inside a builder when I click the button its updates both currentStep value and the widget that should be returned.
Here's the code:
Widget StackPositioned(BuildContext context) {
return Positioned(
top: 260,
width: MediaQuery.of(context).size.width,
child: Container(
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.symmetric(horizontal: 15),
child: Column(
children: <Widget>[
RegistrationHeader(context),
const Gap(20),
RegistrationSteps(context)[currentStep.value],
],
),
),
);
}
List of widgets:
List<Widget> RegistrationSteps(BuildContext context) {
return [
Column(
children: <Widget>[
Label(context, "Tam Adınız"),
const Gap(10),
FullNameInput(context),
const Gap(15),
Label(context, "E-Posta"),
const Gap(10),
EmailInputField(context),
const Gap(15),
Label(context, "Şifreniz"),
const Gap(10),
PasswordInputField(context),
....
And the updater:
TextButton(
onPressed: () => currentStep.value++,
style: TextButton.styleFrom(
backgroundColor: Colors.purple,
minimumSize: Size(MediaQuery.of(context).size.width, 50),
),
child: Text(
"Devam Et",
style: Displays.display2.copyWith(color: Colors.white),
),
),
My integer:
RxInt currentStep = 0.obs;
Edit: I have done this with setState() but, GetX is the project requirement.

Just wrap your RegistrationSteps(context)[currentStep.value],widget with Obx.
like this
Obx(() {
return RegistrationSteps(context)[currentStep.value];
});
It will make RegistrationSteps widget observable. that means if anyvalue changes inside it , it will update itself.

Related

Flutter and provider: How do i make my custom listview update automatically?

What i want to achieve is to have my listview update automatically when the addtoBucketlist fucntion inside the createbucketlistpage is called. Currently the first instance works but when i proceed to add another the listview doesnt update and i have to navigate away from the bucketlistpage and back before i can see the changes.
I am generating a list of widgets on the createbucketlistpage where users can click on a particular widget and it should get added to the bucket list page automatically.
Heres my provider code
class UpcomingTour extends ChangeNotifier {
List<String> upcomingTourImages = [
'assets/UpcomingToursImages/ut1.jpg',
'assets/UpcomingToursImages/ut2.jpg',
'assets/UpcomingToursImages/ut3.jpg',
];
List<String> upComingTourTitle = [
'Kruger',
'Camping and Hiking Ilorin',
'Labadi Beach',
];
List<String> upcomingTourDate = [
'Loading...',
'Loading...',
'Loading...',
];
List<Widget> upcomingTourBody = [
//kruger
const Padding(
padding: EdgeInsets.all(16.0),
child: Text(
'''The big five: Elephant, Lion, Rhino, Leopard and Buffalo. Kruger park is home to a stunning diversity of wildlife, trees, flowers and most importantly The big five. Steeped in legend and history with exquisite accomodations and meals. Kruger national park is one of the most famous tourist destinations in the world. To get the real taste of a safari tour, book a tour to Kruger national park.'''),
),
//camping
const Padding(
padding: EdgeInsets.all(16.0),
child: Text(
'''Enjoy one of the best outdoor activities in the world in the beautiful city of Ilorin.
Our world has changed drastically since the pandemic and many people are feeling more isolated. Work from home has become the norm for many and social media, email and the general 24/7 noise of life can leave us more stressed out.
Disconnecting from technology and reconnecting with nature gives our brain a much-needed recharge, especially among those with sedentary occupations. Camping and Hiking helps you reconnect with nature, build better relationships and aids in overall physical and mental fitness'''),
),
// labadi beach
const Padding(
padding: EdgeInsets.all(16.0),
child: Text(
'''Also known as La pleasure beach. Labadi beach is one of the best beaches on the Ghana coast, filled with lots and lots of fun activities such as games, horse riding and most importantly, relaxing. Labadi beach is perfect for both couple and family vacations.'''),
),
];
var newUpcomingTour;
List<Padding> upcomingTourList = [];
// The original function to generate a list of Upcoming tour widgets
List<Padding> getUpcomingTour(BuildContext context) {
for (int i = 0; i < upcomingTourImages.length; i++) {
String imagePath = upcomingTourImages[i];
String title = upComingTourTitle[i];
String desc = upcomingTourDate[i];
newUpcomingTour = Padding(
padding: const EdgeInsets.all(2.0),
child: Container(
width: MediaQuery.of(context).size.width,
height: 100,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.grey,
),
borderRadius: const BorderRadius.all(Radius.circular(10))),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(right: 10),
child: Row(
children: [
ClipRRect(
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(10),
topLeft: Radius.circular(10)),
child: Image.asset(
imagePath,
fit: BoxFit.cover,
width: 100,
height: 100,
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
title,
style: const TextStyle(
fontSize: 17,
),
),
),
const SizedBox(
height: 10,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(desc),
),
],
),
],
),
),
Flexible(
child: GestureDetector(
onTap: () {
Provider.of<UpcomingTour>(context, listen: false)
.addToBucketList(upcomingTourList[i]);
Navigator.pop(context);
},
child: const Padding(
padding: EdgeInsets.only(right: 5),
child: CircleAvatar(
child: Icon(Icons.add),
),
)),
),
],
),
),
);
upcomingTourList.add(newUpcomingTour);
}
return upcomingTourList;
}
List<Padding> bucketList = [];
void addToBucketList(Padding item) {
bucketList.add(item);
notifyListeners();
}
void removeFromBucketList(Padding item) {
bucketList.remove(item);
notifyListeners();
}
void clearBucketList() {
bucketList.clear();
notifyListeners();
}
}
The code above contains the function that generates the list of widgets that are displayed in the create bucket list page
Heres the code for my create bucketlist page
class _BucketListPageContentsState extends State<BucketListPageContents> {
String searchInput = '';
TextEditingController controller = TextEditingController();
UpcomingTour upcomingTour = UpcomingTour();
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Create your explore bucket list',
style: kBoldTextStyleBlack,
),
addVerticalSpacing(20),
const Text(
'Let’s make your dreams come true. Tell us where you would like to visit or an event you would like to attend and we might just make it happen'),
addVerticalSpacing(20),
const Text(
'Add from upcoming tours',
style: kBoldTextStyleBlack,
),
addVerticalSpacing(20),
],
),
Flexible(
child: ListView(
children: upcomingTour.getUpcomingTour(context),
),
),
],
),
);
}
}
And finLally heres the bucketlistpage
class BucketList extends StatefulWidget {
static const String id = 'Bucketlistpage';
const BucketList({Key? key}) : super(key: key);
#override
State<BucketList> createState() => _BucketListState();
}
class _BucketListState extends State<BucketList> {
int? bucketListlength;
UpcomingTour upcomingTour = UpcomingTour();
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
bucketListlength = Provider.of<UpcomingTour>(context).bucketList.length;
if (bucketListlength == 0) {
return SafeArea(
child: Scaffold(
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.blue,
tooltip: 'Add new tour to Bucketlist',
child: const Icon(
Icons.add,
color: Colors.white,
),
onPressed: () {
Navigator.push(
context,
PageTransition(
child: const CreateBucketListPage(),
type: PageTransitionType.rightToLeft));
}),
body: Align(
alignment: Alignment.center,
child: ListView(scrollDirection: Axis.vertical, children: [
Padding(
padding: const EdgeInsets.all(20.0),
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 0.41,
decoration: const BoxDecoration(
color: Colors.grey,
image: DecorationImage(
fit: BoxFit.fill,
image: AssetImage('assets/emptybucketlist.jpg'),
),
),
),
),
const Align(
alignment: Alignment.center,
child: Text(
'BucketList is Empty',
style: kBoldTextStyleBlack,
),
),
addVerticalSpacing(20),
const Padding(
padding: EdgeInsets.all(10.0),
child: Text(
'Let’s make your dreams come true. Add places/events you woud like to visit/attend. '),
),
]),
),
),
);
} else {
return SafeArea(
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.black),
onPressed: () => Navigator.of(context).pop(),
),
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.blue,
tooltip: 'Add new tour to BucketList',
child: const Icon(
Icons.add,
color: Colors.white,
),
onPressed: () {
Navigator.push(
context,
PageTransition(
child: const CreateBucketListPage(),
type: PageTransitionType.rightToLeft))
.then((value) => setState(() {}));
}),
body: Column(
children: [
Flexible(
child: ListView(
shrinkWrap: true,
key: const Key('upcomingTour'),
children: Provider.of<UpcomingTour>(context, listen: true)
.bucketList,
),
),
ReusableButton(const Text('Clear BucketList'), () {
Provider.of<UpcomingTour>(context, listen: false)
.clearBucketList();
})
],
),
),
);
}
}
}
i cannot use listview.builder because my widgets are already being generated by a fucntion and i dont need to build them as they are being added manually by the user.
Heres my idea is to listen for the changes in the length of the bucketlist list in the provider model and use it to show an empty bucketlist page if the length of the bucketlist list is 0 or show the list.
I am new to provider and ihave tried different things from tutorials but none worked. I even changed from Change notifier to value notifier but it didnt work.
I have also tried different ways to refresh the bucketlist page when i pop from createbucketlistpage but that too doesnt work.
Any help is greatly appreciated.
use Consumer in your page whenever your Data Changes your widget will rebuild automatically (when using notifyListiners method ), also its a performance wise to use Consumer which is rebuild only the widget inside the Consumer not the whole page and that will provide a better performance for your app
here is an example:
Consumer<yourProviderType>(
builder:(context, value, child) => value.yourList.isEmpty
? CircularProgressIndicator():
build a widget which contain data comes from the value instnace
)

Flutter Bottom overflowed by xx pixel

I am trying to construct a page with multiple widgets Row, Column, Expanded, ListView, etc...
I am a bit confused. I want a page scrollable with my widgets ThemeList.
I have the error :
A RenderFlex overflowed by 28 pixels on the bottom.
class SettingsViewState extends State<SettingsView> {
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
drawer: const NavDrawer(),
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.settingsTitle),
backgroundColor: Theme.of(context).primaryColor,
),
body: CustomScrollView(slivers: [
SliverFillRemaining(
child: Column(
children: const [
ThemeList(),
SizedBox(height: 8),
ThemeList(),
SizedBox(height: 8),
ThemeList(),
],
),
),
]),
);
}
}
class ThemeList extends StatelessWidget {
const ThemeList({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.all(10),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Theme.of(context).primaryColor, width: 2),
borderRadius: BorderRadius.circular(10),
),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 5, left: 20),
child: Text(
AppLocalizations.of(context)!.settingsThemeSubTitle,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 23,
),
),
)
],
),
ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(8),
itemCount: AppTheme.values.length,
itemBuilder: (context, index) {
final itemAppTheme = AppTheme.values[index];
var nameTheme = itemAppTheme.toString()
return Card(
color: appThemeData[itemAppTheme]?.primaryColor,
child: ListTile(
title: Text(
nameTheme,
style: appThemeData[itemAppTheme]?.textTheme.bodyText1,
),
onTap: () {
BlocProvider.of<ThemeBloc>(context).add(
ThemeChanged(theme: itemAppTheme),
);
Preferences.saveTheme(itemAppTheme);
},
),
);
},
)
],
),
),
),
);
}
}
Desired result :
Just wrap the ListView.builder() inside ThemeList with an Expanded and the problem would vanish.
If you want to have all the items inside each ThemeList displayed with a scroll for the whole screen then the easiest why is to do the following:
Change the CustomScrollView in the body of the Scaffold to be SingleChildScrollView with the Column as its child.
Remove the Expanded at the start of ThemeList.
Remove the ListView.builder() inside the ThemeList and replace it with any looping logic to directly render the cards, for example:
...AppTheme.values.map((itemAppTheme) {
var nameTheme = itemAppTheme.toString();
return Card(
color: appThemeData[itemAppTheme]?.primaryColor,
child: ListTile(
title: Text(
nameTheme,
style: appThemeData[itemAppTheme]?.textTheme.bodyText1,
),
onTap: () {
BlocProvider.of<ThemeBloc>(context).add(
ThemeChanged(theme: itemAppTheme),
);
Preferences.saveTheme(itemAppTheme);
},
),
);
}).toList()

How to remove padding of MaterialBanner?

I want to remove the following blue padding from MaterialBanner widget, but it doesn't seem to be customizable. I want to insert an image in the red region.
I looked into MaterialBanner for using across Scaffold widgets because ScaffoldMessenger doesn't allow me to insert widgets other than MaterialBanner.
Is there any suggestion?
dartpad.
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: Scaffold(body: JustBanner())));
}
class JustBanner extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _JustBannerState();
}
}
class _JustBannerState extends State<JustBanner> {
#override
Widget build(BuildContext context) {
return Column(
children: [
ElevatedButton(
onPressed: () {
final messenger = ScaffoldMessenger.of(context);
messenger.clearMaterialBanners();
messenger.showMaterialBanner(MaterialBanner(
padding: EdgeInsets.zero,
leadingPadding: EdgeInsets.zero,
leading: const SizedBox.shrink(),
backgroundColor: Colors.blue,
content: Container(
color: Colors.red,
width: 200,
height: 50,
),
actions: const [SizedBox.shrink()]));
},
child: const Text('Banner')),
],
);
}
}
Container(
width: MediaQuery.of(context).size.width,
child: MaterialBanner(
content: Text('Hello'),
actions: [
Icon(Icons.add),
],
),
),
Its no possible without copy and re-create the class, buttonBar always appear:
final Widget buttonBar = Container( // <-- problematic widget
alignment: AlignmentDirectional.centerEnd,
constraints: const BoxConstraints(minHeight: 52.0),
padding: const EdgeInsets.symmetric(horizontal: 8),
child: OverflowBar(
overflowAlignment: widget.overflowAlignment,
spacing: 8,
children: widget.actions,
),
);
final double elevation = widget.elevation ?? bannerTheme.elevation ?? 0.0;
final Color backgroundColor = widget.backgroundColor
?? bannerTheme.backgroundColor
?? theme.colorScheme.surface;
final TextStyle? textStyle = widget.contentTextStyle
?? bannerTheme.contentTextStyle
?? theme.textTheme.bodyText2;
Widget materialBanner = Container(
margin: EdgeInsets.only(bottom: elevation > 0 ? 10.0 : 0.0),
child: Material(
elevation: elevation,
color: backgroundColor,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: padding,
child: Row(
children: <Widget>[
if (widget.leading != null)
Padding(
padding: leadingPadding,
child: widget.leading,
),
Expanded(
child: DefaultTextStyle(
style: textStyle!,
child: widget.content,
),
),
if (isSingleRow)
buttonBar, // <----- here
],
),
),
if (!isSingleRow)
buttonBar, // <----- here
if (elevation == 0)
const Divider(height: 0),
],
),
),
);

flutter opening a drawer using a button inside a scaffold body

What I have
A custom button with onpress to open a drawer, my build snippet : (inside MyClassState)
Widget build(BuildContext context) => Scaffold(
key: _key,
body: Container(
child: Column(
children: [
Row(children: [
ElevatedButton(
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Icon(
Icons.settings,
size: 38,
color: Colors.white,
),
),
onPressed: () => _key.currentState?.openEndDrawer(),
),
]),]),))
method globalkey _key is used (after reading some solution here)
Class MyClassState extends State<MyClass> {
GlobalKey<ScaffoldState> _key = GlobalKey();
...
}
What I expected
The drawer opens on press/tap
The current behaviour result
Nothing happens on tap, but I can open the drawer using slide gesture.
What did I do wrong in this case?
You didn't declared endDrawer in scaffold, Here is the your updated code
Widget build(BuildContext context) => Scaffold(
key: _key,
endDrawer: Drawer( /// this is missing in your code
child: Container(
width: 200,
color: Colors.red,
),
),
body: Container(
child: Column(
children: [
Row(children: [
ElevatedButton(
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Icon(
Icons.settings,
size: 38,
color: Colors.white,
),
),
onPressed: () => _key.currentState?.openEndDrawer(),
),
]),]),))

How to display loading widget until a main widget is loaded

I am using the animation package to create a popup modal However building the widget inside the model is very noticeably slow and is making making the popup take time to open. I am trying to put a loading indicator when opening the modal then build the widget afterward and just update.
I am lost on how to accomplish that.. it would be highly appreciated if someone could help.
this is the animation package method for the modal
ElevatedButton(
onPressed: () {
showModal<void>(
context: context,
builder: (BuildContext context) {
// building _ExampleAlertDialog takes time
return _ExampleAlertDialog(loading: loading);
},
).then((state) => setState(() => {loading = !loading}));
},
child: const Text('SHOW MODAL'),
),
the _ExampleAlertDialog is supposed to be a listView
class _ExampleAlertDialog extends StatefulWidget {
_ExampleAlertDialog({
this.loading,
});
final bool loading;
#override
__ExampleAlertDialogState createState() => __ExampleAlertDialogState();
}
class __ExampleAlertDialogState extends State<_ExampleAlertDialog> {
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(vertical: 50, horizontal: 20),
child: Expanded(
child: ClipRRect(
borderRadius: BorderRadius.circular(5),
child: Material(
child: Column(
children: [
Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Theme.of(context).dividerColor))),
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [Icon(Icons.arrow_back), Icon(Icons.refresh)],
),
),
// This is the Listview I am trying to avoid onLoad
Expanded(child: widget.loading == true ? Container():
ListView.builder(
itemCount: 16,
itemBuilder: (BuildContext ctxt, int index) {
return Container(
// width: MediaQuery.of(context).size.width * 1,
child: Row(
children: <Widget>[
Icon(
Icons.radio_button_unchecked,
color: Colors.blue,
size: 12.0,
),
SizedBox(
width: 20.0,
),
Text(
"Travetine",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w400),
),
],
),
);
},
)
)
],
),
),
),
),
);
}
}