How can i turn container to Floatting action button - flutter

i am developin app i just got some of those code from an app i just need to cart page button make a floating action button this button shows how many products in cart also i want to do this if counter=0 i need to hide that FloatingActionButton if they added item to basket just show that time if you have any suggestions thanks a lot for now
'class CustomAppBar extends StatelessWidget {
#override
Widget build(BuildContext context) {
final CartListBloc bloc = BlocProvider.getBloc<CartListBloc>();
// TODO: implement build
return Container(
margin: EdgeInsets.only(bottom: 5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
StreamBuilder(
stream: bloc.listStream,
builder: (context, snapshot) {
List<FoodItem> foodItems = snapshot.data;
int length = foodItems != null ? foodItems.length : 0;
return buildGestureDetector(length, context, foodItems);
},
)
],
),
);
}
GestureDetector buildGestureDetector(
int length, BuildContext context, List<FoodItem> foodItems) {
return GestureDetector(
onTap: () {
if (length > 0) {
Navigator.push(
context, MaterialPageRoute(builder: (context) => Cart()));
} else {
return;
}
},
child: Container(
margin: EdgeInsets.only(left: 30),
child: Text(length.toString()),
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Colors.yellow[800], borderRadius: BorderRadius.circular(50)),
),
);
}'

First, you need a stateful widget and not a stateless one. If you are using VS code, it can actually do it for you automatically.
Once you have that, one variable will be "length".
String length;
void initState() {
length=0;
}
Whenever you increment it up, you have to call...
setState(() {
length+=1;
});
or down...
setState(() {
length-=1;
});
your floating action button will be...
(length==0)?
Container():
GestureDetector(
onTap: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) => Cart()));
},
child: Container(
margin: EdgeInsets.only(left: 30),
child: Text(length.toString()),
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Colors.yellow[800], borderRadius: BorderRadius.circular(50)),
),
);
This means that if the cart length is 0, it will show an empty container.
If the cart is not 0, it will show a button with length

Related

Can't update ListView inside showModalBottomSheet after delete from db

I have a ModalBottomSheet and I have a ListView inside it. ListView is connected to a sqlite db. All create,read and delete methods are working fine but when I delete an item, I get the toast message which approves the operation but my view does not get updated. When I close the sheet and open again It's updated.
My ModalBottomSheet codes are :
void popUpScreen(context) {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
color: Colors.blue[600],
height: MediaQuery.of(context).size.height * .8,
child: ListView.builder(
itemCount: _loclist.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.only(top: 8, left: 16, right: 16),
child: Card(
child: ListTile(
leading: Text(_loclist[index].name),
title: IconButton(
icon: Icon(Icons.delete),
onPressed: () async {
var result = await locationdbservice
.deleteLocation(_loclist[index].id);
this._loclist.removeAt(index);
if (result > 0) {
Toast.show("Silindi", context,
duration: Toast.LENGTH_SHORT,
gravity: Toast.BOTTOM);
getLocations();
}
},
),
),
));
}));
});
}
and getLocations() :
getLocations() async {
_loclist = List<Loc>();
var locations = await locationdbservice.readLocations();
locations.forEach((location) {
setState(() {
var locationModel = Loc();
locationModel.lat = location['lat'];
locationModel.lon = location['lon'];
locationModel.name = location['name'];
locationModel.note = location['note'];
locationModel.id = location['id'];
_loclist.add(locationModel);
});
});
}
I tried to write an initState() function which returns getLocations() and called it at the end of onPressed() with initState() but didn't work. I tried to make an empty initState() but didn't work. How can I update my ListView while I view it?
Edit: Here's where I call popUpScreen:
Widget locationsButton(BuildContext context) {
return Container(
width: 250,
height: 60,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
padding: EdgeInsets.zero,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
),
clipBehavior: Clip.antiAlias,
onPressed: () {
popUpScreen(context);
},
child: Ink(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [Colors.white70, Colors.white38])),
child: Container(
constraints: BoxConstraints(maxHeight: 300, minWidth: 50),
alignment: Alignment.center,
child: Text(
"KONUMLARIM",
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
),
),
));
}
Here I come with the solution. It happens always with showModelBottomSheet that it doesn't rebuild or change its state so I come across the solution. Add StatefulBuilder in ModelBottomSheet which will change its state onPressed Function. Also, your code is throwing exceptions so I handled these exceptions. Go to GitHub merge pull request and continue.
Thumbs up if this solution helped
Here is the sample code. I had implemented this just go to GitHub and start coding
void popUpScreen() {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter mystate) {
return Container(
color: Colors.blue[600],
height: MediaQuery.of(context).size.height * .8,
child: ListView.builder(
itemCount: _loclist.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.only(top: 8, left: 16, right: 16),
child: Card(
child: ListTile(
leading: Text(_loclist[index].name),
title: IconButton(
icon: Icon(Icons.delete),
onPressed: () async {
var result = await locationdbservice
.deleteLocation(_loclist[index].id);
mystate(() {
this._loclist.removeAt(index);
});
if (result > 0) {
Toast.show("Silindi", context,
duration: Toast.LENGTH_SHORT,
gravity: Toast.BOTTOM);
getLocations();
}
setState(() {});
},
),
),
));
}));
});
});
}
Please check with setstate method
void popUpScreen(context) {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
color: Colors.blue[600],
height: MediaQuery.of(context).size.height * .8,
child: ListView.builder(
itemCount: _loclist.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.only(top: 8, left: 16, right: 16),
child: Card(
child: ListTile(
leading: Text(_loclist[index].name),
title: IconButton(
icon: Icon(Icons.delete),
onPressed: () async {
var result = await locationdbservice
.deleteLocation(_loclist[index].id);
setState(() {
this._loclist.removeAt(index);
});
if (result > 0) {
Toast.show("Silindi", context,
duration: Toast.LENGTH_SHORT,
gravity: Toast.BOTTOM);
getLocations();
}
},
),
),
));
}));
});
}
According to the docs:
Calling setState notifies the framework that the internal state of
this object has changed in a way that might impact the user interface
in this subtree, which causes the framework to schedule a build for
this State object.
So if the state of the widget changes you have to call setState((){}); to trigger a rebuild of the view and see immediately the changes implied by the new state.

Return variable from current screen to previous screen

So I am implementing a 'settings' view in my Flutter app. The idea is all settings will appear in a ListView, and when the user will click on a ListTile, a showModalBottomSheet will pop where the user will be able to manipulate the setting. The only problem I am having is I am unable to migrate the showModalBottomSheet to a separate class as I cannot make the new function (outside the class) return the manipulated setting variable. This has lead to a messy code, all in a single class.
class Page extends StatefulWidget {
Page({Key key}) : super(key: key);
#override
_Page createState() => _Page();
}
class _Page extends State<Page> {
var value;
#override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
ListTile(
title: Text("Age"),
trailing: Text(value),
onTap: () {
setState(() {
value = _valueSelector(); // This doesn't work, but to give an idea what I want
});
},
),
],
);
}
}
int _valueSelector(context) { // Doesn't return
var age = 0;
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Wrap(
children: [
Column(
children: <Widget>[
Slider(
value: age.toDouble(),
min: 0,
max: 18,
divisions: 18,
onChanged: (value) {
setState(() {
age = value.toInt();
});
},
),
],
),
],
);
});
},
).whenComplete(() {
return age; // Not sure if return is supposed to be here
});
}
How can I implement showModalBottomSheet in a separate class and just make it return the variable representing the setting chosen by the user?
You can try the below code,
First, create a class custom_bottom_sheet.dart and add the below code. You can use it everywhere in the project. And also use this library modal_bottom_sheet: ^0.2.0+1 to get the showMaterialModalBottomSheet.
customBottomSheet(BuildContext context, {#required Widget widget}) async {
return await showMaterialModalBottomSheet(
context: context,
backgroundColor: AppColors.transparent_100,
barrierColor: AppColors.black_75,
isDismissible: false,
enableDrag: true,
builder: (_, ScrollController scrollController) {
return widget;
},
);
}
Sample example code:
Create another class called bottom_sheet_example.dart and add the below code.
class BottomSheetExample {
static Future getSheet(BuildContext _context,
{ValueChanged<bool> onChanged}) async {
await customBottomSheet(
_context,
widget: SafeArea(
child: Container(
padding: EdgeInsets.only(left: 40.0, right: 40.0),
height: 170.0,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(27.0),
topRight: Radius.circular(27.0))),
child: Container(
padding: EdgeInsets.only(top: 32),
child: Column(
children: [
Text("Were you at Queen Victoria Building?"),
SizedBox(height: 48),
Row(
children: [
Expanded(
child: RaisedButton(
child: Text("No"),
onPressed: () {
Navigator.of(_context).pop();
onChanged(false);
},
),
),
SizedBox(width: 18),
Expanded(
child: RaisedButton(
child: Text("Yes"),
onPressed: () {
Navigator.of(_context).pop();
onChanged(true);
},
),
),
],
),
SizedBox(height: 24),
],
),
),
)),
);
}
}
Button click to show the bottom sheet
#override
Widget build(BuildContext context) {
return Scaffold(
body: yourBodyWidget(),
bottomNavigationBar: Container(
height: 40,
width: double.infinity,
child: FlatButton(
onPressed: () {
/// call BottomSheetExample class
BottomSheetExample.getSheet(
context,
onChanged: (bool result) async {
///
/// add your code
},
);
},
child: Text("show bottom sheet")),
),
);
}
In onChanged callback you can return your value(obj/String/num/bool/list).
Thank you!

Flutter - Returning to previous page from AppBar is not refreshing the page, with Navigator.pop(context)

I was trying to get the list page refreshed if a method was run on another page. I do pass the context using the push navigation.
I tried to follow these 3 answers Answer 1 Answer 2 and Answer 3 and I am not able to manage the states here.
This is the first list page which needs to be refreshed. It calls a class
class _PageLocalState extends State<PageLocal> {
#override
Widget build(BuildContext context) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: SafeArea(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: widget.allLocal.length,
//padding: const EdgeInsets.only(top: 10.0),
itemBuilder: (context, index) {
return LocalCard(widget.allLocal[index]);
},
)),
)
],
),
);
}
}
The next class:
class LocalCardState extends State<LocalCard> {
FavData localdet;
LocalCardState(this.localdet);
ListTile makeListTile() => ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
title: Text(
localdet.name,
style: TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text(localdet.loc),
trailing: Icon(Icons.keyboard_arrow_right, size: 30.0),
onTap: () => navigateToDetail(localdet),
);
Widget get localCard {
return new Card(
elevation: 4.0,
margin: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: Container(
child: makeListTile(),
));
}
#override
Widget build(BuildContext context) {
return new Container(
child: localCard,
);
}
navigateToDetail(FavData localdet) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FavouriteDetailPage(
mndet: localdet,
)));
setState(() {});
}
}
Now this is routing to the final detail page:
class _FavouriteDetailPageState extends State<FavouriteDetailPage> {
bool isFav = false;
FavData mndet;
_FavouriteDetailPageState(this.mndet);
// reference to our single class that manages the database
final dbHelper = DatabaseHelper.instance;
#override
Widget build(BuildContext context) {
Widget heading = new Container(...);
Widget middleSection = new Expanded(...);
Widget bottomBanner = new Container(...);
Widget body = new Column(...);
final makeBottom = Container(
height: 55.0,
child: BottomAppBar(
color: Color.fromRGBO(36, 36, 36, 1.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new FavIconWidget(mndet),
],
),
),
);
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('The Details'),
backgroundColor: Color.fromRGBO(36, 36, 36, 1.0),
),
body: Container(
child: Card(
elevation: 5.0,
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.white70, width: 1),
borderRadius: BorderRadius.circular(10),
),
margin: EdgeInsets.all(20.0),
child: Padding(
padding: new EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0),
child: body,
),
),
),
bottomNavigationBar: makeBottom,
);
}
void share(BuildContext context, FavData mndet) {
final RenderBox box = context.findRenderObject();
final String shareText = "${mndet.name} - ${mndet.desc}";
Share.share(shareText,
subject: mndet.loc,
sharePositionOrigin: box.localToGlobal(Offset.zero) & box.size);
}
}
class FavIconWidget extends StatefulWidget {
final FavData mnforIcon;
FavIconWidget(this.mnforIcon);
#override
_FavIconWidgetState createState() => _FavIconWidgetState();
}
class _FavIconWidgetState extends State<FavIconWidget> {
final dbHelper = DatabaseHelper.instance;
Future<bool> get isFav async {
final rowsPresent = await dbHelper.queryForFav(widget.mnforIcon.id);
if (rowsPresent > 0) {
print('Card Loaded - Its Favourite already');
return false;
} else {
print('Card Loaded - It is not favourite yet');
return true;
}
}
void _insert() async {...}
void _delete() async {...}
#override
Widget build(BuildContext context) {
return FutureBuilder<bool>(
future: isFav,
initialData:
false, // you can define an initial value while the db returns the real value
builder: (context, snapshot) {
if (snapshot.hasError)
return const Icon(Icons.error,
color: Colors.red); //just in case the db return an error
if (snapshot.hasData)
return IconButton(
icon: snapshot.data
? const Icon(Icons.favorite_border, color: Colors.white)
: Icon(Icons.favorite, color: Colors.red),
onPressed: () => setState(() {
if (!snapshot.data) {
print('Its favourite so deleting it.');
_delete();
} else {
print('Wasnt fav in the first place so inserting.');
_insert();
}
}));
return CircularProgressIndicator(); //if there is no initial value and the future is not yet complete
});
}
}
I am sure this is just some silly coding I have done but just not able to find out. Where.
I tried adding Navigator.pop(context); in different sections of the detail page and it fails.
Currently, I have to navigate back to the Favourites list page and then HomePage and then back to Favourites ListPage to refresh the list.
try this.. Anywhere you are using Navigator.pop or Navigator.push .. Instead of this use this:
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (BuildContext context) => Password())
);
//instead of Password use the name of the page(the second page you want to go to)

How can i show a modal below another modal in Flutter?

So, i'm currently developing a "to-do list" app using Flutter. This app has a floating button that, when pressed, shows a modal bottom sheet with a few other buttons. Some of those buttons, when pressed, also returns modal bottom sheets with options for the user to choose from. The thing is, i can't seem to find a way to place the secondary bottom sheet directly below the primary bottom sheet. In other words, i want to make the primary modal resize to avoid being overlapped by the secondary modal. Is that possible on flutter?
Here's what the app should look like
And here's what it currently looks like
Here's the code example for the primary modal bottom sheet:
taskModal(BuildContext context) {
return showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topRight: Radius.circular(20), topLeft: Radius.circular(20)),
color: Colors.white,
),
child: Padding(
padding:
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
_buildCancelButton(),
TaskForm(),
BuildBorder(),
PriorityButton(),
BuildBorder(),
DateButton(),
BuildBorder(),
_buildConfirmButton(context)
],
),
),
),
);
},
);
}
And here is the code example for one of the buttons i've mentioned before (the priority button, specifically):
class PriorityButton extends StatefulWidget {
#override
_PriorityButtonState createState() => _PriorityButtonState();
}
class _PriorityButtonState extends State<PriorityButton> {
List<String> _priorities = [
'Nenhuma',
'Baixa (!)',
'Média (!!)',
'Alta (!!!)',
];
String _settledPriority = 'Selecionar';
#override
Widget build(BuildContext context) {
return GestureDetector(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(horizontal: 6),
child: Icon(
Icons.flag,
color: Color(0xff9DA1A6),
)),
Text("Prioridade"),
Expanded(
child: Align(
alignment: Alignment.centerRight,
child: Padding(
padding: EdgeInsets.only(
right: MediaQuery.of(context).size.width * 0.04),
child: Text(_settledPriority,
maxLines: 1),
),
),
),
],
),
onTap: () async => await _buildBottomSheet(),
);
}
_setPriority(String priority) {
setState(() {
_settledPriority = priority;
});
Navigator.pop(context);
}
_buildBottomSheet() {
return showModalBottomSheet(
context: context,
builder: (context) {
return Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: _priorities.length,
itemBuilder: (context, index) => GestureDetector(
child: Text(
_priorities\[index\],
),
onTap: () => _setPriority(_priorities\[index\]),
),
),
);
},
);
}
}
What you can do here is fetch the height of the bottom modal with LayoutBuilder, then use this value as padding for the first displayed modal. Though by design, the modal seems to appear in a single BottomSheet. Another approach that you can look into is by populating a single modal instead of using two modal on the screen.

How to update state from static method in Flutter?

I have a bottom nav bar that uses this code to switch widgets:
void _onItemTapped(int index) {
setState(() {
selectedIndexGlobal = index;
print(index);
});
}
And shows the needed widgets in body:
body: SafeArea(
child: new LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
Container con = Container(
child: _widgetOptions.elementAt(selectedIndexGlobal)
);
return con;
}
))
I also have a GridView with static children list loaded from Firebase, and tried to apply a GestureDetector to it, to change selectedIndex and update screen state:
static getExpenseItems(AsyncSnapshot<QuerySnapshot> snapshot, BuildContext context) {
try {
snapshot.data.documents.sort((a, b) =>
a.data["order"].compareTo(b.data["order"]));
} on NoSuchMethodError {}
return snapshot.data.documents
.map((doc) =>
new GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
catName = doc["catName"];
setState(() {
selectedIndexGlobal = 4;
});
},
child: new Container(
child: Container(
padding: EdgeInsets.only(top: 15, left: 10, bottom: 15),
decoration: new BoxDecoration(
border: new Border.all(color: Colors.grey[200],
width: 0.8)),
child: Align(
alignment: Alignment.centerLeft,
child: Text(doc["name"],
style: TextStyle(fontSize: categoriesFont),
textAlign: TextAlign.center),
)
)
)
)
).toList();
}
But setState can't be called from a static method, as well as GridView.count doesn't let me use non-static widgets as child. What should I do then to update state on click?
This line will help you get the instance of the Widget State object you need.
All you need access to is the current context object:
_HomeState stateObject = context.findAncestorStateOfType<_HomeState>();
stateObject.setState(() {
_tabIndex = index;
});
For this to work, ensure that the context you use to access the State Object is from a child widget of the State object you are trying to find.