i am trying to use CurveBottomNavigatorBar in my flutter project...
i used my Curve... in the main dart like this:
class _BottomNavigatorBarState extends State<BottomNavigatorBar> {
int selectedIndex = 0;
final screen = [
HomeScreen(),
CartScreen(),
MyStore(),
SettingScreen(),
];
#override
Widget build(BuildContext context) {
int args = ModalRoute.of(context).settings.arguments;
return Scaffold(
body: screen[selectedIndex],
bottomNavigationBar: CurvedNavigationBar(
height: 55,
backgroundColor: Colors.transparent,
buttonBackgroundColor: Theme.of(context).accentColor,
color: Theme.of(context).primaryColor,
animationCurve: Curves.easeOutCubic,
index: selectedIndex,
items: <Widget>[
// code here
],
onTap: (index) {
setState(() {
args = selectedIndex;
selectedIndex = index;
});
},
),
);
}
}
when i go to my store screen there is an add button that take me to a Form to complete it but when i go back it doesn't show me the BOTNAVBAR on my screen so i though that if i can pass arguments so when i pushback to my main it open my my stores automatically
this is the adding store page:
#override
Widget build(BuildContext context) {
int index = 2;
_onBackPressed() {
Navigator.of(context).pushReplacementNamed(
'/myStore',
arguments: index,
);
}
return WillPopScope(
onWillPop: () {
return _onBackPressed();
},
child: Scaffold(
appBar: AppBar(
title: Text('Add Store'),
leading: IconButton(
onPressed: () {
Navigator.of(context).pushReplacementNamed(
'/myStore',
arguments: index,
);
},
icon: Icon(Icons.arrow_back),
),
),
sp i did it like that and i know its wrong cause its not working
anyhelp please and excuse my bad english :(
Please refer this : https://flutter.dev/docs/cookbook/navigation/navigate-with-arguments
Navigator.pushNamed(
context,
ExtractArgumentsScreen.routeName,
arguments: MyStore(index),
);
Make your MyStore() widget that accepts argument like,
int Index;
MyStore(this.Index)
and use in that screen like,
widget.Index
Please go through above link once.
You have a parameters arguments which you can pass to pushReplacementName:
Future<T?> pushReplacementNamed <T extends Object?, TO extends Object?>(
BuildContext context,
String routeName,
{
TO? result,
Object? arguments,
}
)
You can call it like this:
// Considering a variable named my_args_obj
Navigator.of(context).pushReplacementNamed('/myStore', arguments: my_args_obj);
Then to get it from your page:
final args = ModalRoute.of(context).settings.arguments;
Related
App is a simple memory/guessing game with a grid of squares. Floating action button triggers a "New game" dialog, and a Yes response triggers setState() on the main widget. The print() calls show it is building all the Tile widgets in the grid, but as it returns, the old grid values are still showing. Probably done something stupid but not seeing it. Basic code is below. TIA if anyone can see what is missing/invalid/broken/etc.
Main.dart is the usual main() that creates a stateless HomePage which creates a stateful widget which uses this State:
class MemHomePageState extends State<MemHomePage> {
GameBoard gameBoard = GameBoard();
GameController? gameController;
int gameCount = 0, winCount = 0;
#override
void initState() {
super.initState();
gameController = GameController(gameBoard, this);
}
#override
Widget build(BuildContext context) {
if (kDebugMode) {
print("MemHomepageState::build");
}
gameBoard.newGame(); // Resets secrets and grids
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: GridView.count(
crossAxisCount: Globals.num_columns,
children: List.generate(Globals.num_columns * Globals.num_rows, (index) {
int x = index~/Globals.NR, y = index%Globals.NR;
int secret = gameBoard.secretsGrid![x][y];
var t = Tile(x, y, Text('$secret'), gameController!);
gameBoard.tilesGrid![x].add(t);
if (kDebugMode) {
print("Row $x is ${gameBoard.secretsGrid![x]} ${gameBoard.tilesGrid![x][y].secret}");
}
return t;
}),
),
// Text("You have played $gameCount games and won $winCount."),
),
floatingActionButton: FloatingActionButton(
onPressed: () => newGameDialog("Start a new game?"),
tooltip: 'New game?',
child: const Icon(Icons.refresh_outlined),
),
);
}
/** Called from the FAB and also from GameController "won" logic */
void newGameDialog(String message) {
showDialog<void>(
context: context,
barrierDismissible: false, // means the user must tap a button to exit the Alert Dialog
builder: (BuildContext context) {
return AlertDialog(
title: Text("New game?"),
content: Text(message),
//),
actions: <Widget>[
TextButton(
child: const Text('Yes'),
onPressed: () {
setState(() {
gameCount++;
});
Navigator.of(context).pop();
},
),
TextButton(
child: const Text('No'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
}
);
}
The Tile class is a StatefulWidget whose state determines what that particular tile should show:
import 'package:flutter/material.dart';
import 'gamecontroller.dart';
enum TileMode {
SHOWN,
HIDDEN,
CLEARED,
}
/// Represents one Tile in the game
class Tile extends StatefulWidget {
final int x, y;
final Widget secret;
final GameController gameController;
TileState? tileState;
Tile(this.x, this.y, this.secret, this.gameController, {super.key});
#override
State<Tile> createState() => TileState(x, y, secret);
setCleared() {
tileState!.setCleared();
}
}
class TileState extends State<Tile> {
final int x, y;
final Widget secret;
TileMode tileMode = TileMode.HIDDEN;
TileState(this.x, this.y, this.secret);
_unHide() {
setState(() => tileMode = TileMode.SHOWN);
widget.gameController.clicked(widget);
}
reHide() {
print("rehiding");
setState(() => tileMode = TileMode.HIDDEN);
}
setCleared() {
print("Clearing");
setState(() => tileMode = TileMode.CLEARED);
}
_doNothing() {
//
}
#override
Widget build(BuildContext context) {
switch(tileMode) {
case TileMode.HIDDEN:
return ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.teal,
),
onPressed: _unHide,
child: Text(''));
case TileMode.SHOWN:
return ElevatedButton(
onPressed: _doNothing,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
),
child: secret);
case TileMode.CLEARED:
return ElevatedButton(
onPressed: _doNothing,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.black12,
),
child: const Icon(Icons.check));
}
}
}
it looks like you are calling the following in your build function. That would cause everything to reset everytime it builds. Perhaps it belongs in init instead?
gameBoard.newGame(); // Resets secrets and grids
The original problem is that the Tile objects, although correctly created and connected to the returned main widget, did not have distinct 'key' values so they were not replacing the originals. Adding 'key' to the Tile constructor and 'key: UniqueKey()' to each Tile() in the loop, solved this problem. It exposed a related problem but is out of scope for this question. See the github link in the OP for the latest version.
I am building an app with a bottom appbar with classic indexed pages to navigate the main menu:
class OverAllScreen extends StatefulWidget {
final int initialPage;
OverAllScreen(this.initialPage);
#override
_OverAllScreenState createState() => _OverAllScreenState();
}
class _OverAllScreenState extends State<OverAllScreen> {
List _pageOptions = [
Shop(),
Home(),
Discover(),
Account(),
];
int _page;
#override
void initState() {
super.initState();
setState(() {
_page = widget.initialPage;
});
}
#override
Widget build(BuildContext context) {
final _theme = Theme.of(context);
final _mediaSize = MediaQuery.of(context).size;
return Scaffold(
body: _pageOptions[_page],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
backgroundColor: _theme.primaryColor,
selectedItemColor: _theme.accentColor,
unselectedItemColor: Colors.white,
currentIndex: _page,
onTap: (index) {
setState(() {
_page = index;
});
},
items: [
BottomNavigationBarItem(icon: Icon(Icons.shop), label: 'Shop'),
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.search), label: 'Discover'),
BottomNavigationBarItem(
icon: Icon(Icons.person_outline), label: 'Account'),
],
),
);
}
}
In the account page, further down the widget tree I have a widget that shows a list of detailed products.
I want this page to open up when I click on an item of a simple (non-detailed) grid of products.
This I can easily do with Navigator.of(context).push(MaterialPageR0ute(...))). However I would like to keep the bottomAppBAr visible (like instagram when you look at the products of a user).
The problem is that I have the specific list of products in down in the widget tree, so I can't pass them as an argument at the occount page level, without passing them back each step of the way by passing a function as an argument of the widget.
class ProductList extends StatelessWidget {
#override
Widget build(BuildContext context) {
var _products = Provider.of<List<ProductModel>>(context);
return ListView.builder(
itemExtent: 150,
scrollDirection: Axis.horizontal,
itemCount: _products.length,
itemBuilder: (context, index) => Card(
margin: const EdgeInsets.all(5),
child: InkWell(
child: ProductTile(_products[index]),
onTap: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ProductDetailList(
productIndex: index,
products: _products,
),
),
),
),
),
);
}
}
Here I use MaterialPageRoute, but would like to keep BottomAppBar visible.
Thank you.
To achieve the functionality you are looking for, you would have to write several switch and break case which can be cumbersome as your use is going to be very basic.
What I would suggest is go for this package
This package provide a persistent bottom navigaton bar which is highly customisable and you could even use salmon like bottom bar without writing a single line of extra code.
This is my HomePage.dart Codes. Here I Want to access the _pushSaved() function into the 'onpressed' property from a different file named "randomwords.dart" file (code attached for review). It seems I need to declare the _pushSaved() somewhere but do not have any idea as I am new to dart and flutter. I Will be thankful if somebody could help. Thanks in advance.
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("StartUp Name Generator"),
actions: [
IconButton(icon: Icon(Icons.list), **onPressed: _pushSaved**)
],
),
body: RandomWords(),
);
}
}
The _pushSaved() function code in the randomwords.dart file needs to be accessed in the HomePage.dart as object...
class RandomWords extends StatefulWidget {
#override
_RandomWordsState createState() => _RandomWordsState();
}
class _RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _saved = Set<WordPair>();
final _biggerFont = TextStyle(fontSize: 20.0, color: Colors.pink);
#override
Widget build(BuildContext context) {
return ListView.builder(
itemBuilder: (context, i) {
if (i.isOdd) return Divider();
final index = i ~/ 2;
if (index >= _suggestions.length) {
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
},
);
}
Widget _buildRow(WordPair pair) {
final alreadySaved = _saved.contains(pair);
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
trailing: Icon(
alreadySaved ? Icons.favorite : Icons.favorite_border,
color: alreadySaved ? Colors.red : null,
),
onTap: () {
setState(() {
if (alreadySaved) {
_saved.remove(pair);
} else {
_saved.add(pair);
}
});
},
);
}
void **_pushSaved()** {
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext context) {
final tiles = _saved.map(
(WordPair pair) {
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
);
},
);
final divided =
ListTile.divideTiles(tiles: tiles, context: context).toList();
return Scaffold(
appBar: AppBar(
title: Text("Saved Suggestions"),
),
body: ListView(
children: divided,
),
);
},
),
);
}
}
Although the answer above solves your problem , but thats not a good practice , use absctract classes for defining functions that are supposed to be used at various places.
Your function is defined as a private property. You need to change that first.
pushSaved()
Secondly, You need to define this function as a static function to access it in other classes.
static void pushSaved()
Now call your function in your HomePage class in onPressed function like this:
onPressed: () => RandomWords.pushSaved
Import the HomePage.dart in the Homepage file like this;
import 'package:main.dart' as main; //it doesn't have to be as 'main'
and then acces the function like this:
IconButton(icon: Icon(Icons.list),*onPressed: main._pushSaved)
Note: now when I've done somthing like this and my function startes with a _ it unfortunantly doesn't work, I know that everyone makes functions with _ first, but apperantly it didn't work for me when importing it from another file. So beware of that.
Also the function has to be out of the _RandomWordsState to be accesed by the other file. Just put it down underneath.
I want to load pages from a List and when the user taps on an item from the drawer he can go to that page (if it's already opened) otherwise the Widget will load in the selected page.
But I can't find if that widget is already exists in the List if(myList.contains(Widget1())) => print('it exist'); One guy told me to override hashCode and operator==
class Widget6 extends StatelessWidget {
final String title = 'Widget6';
final Icon icon = Icon(Icons.assessment);
#override
Widget build(BuildContext context) {
return Center(
child: icon,
);
}
#override
bool operator ==(dynamic other) {
final Widget6 typedOther = other;
return title == typedOther.title && icon == typedOther.icon;
}
#override
int get hashCode => hashValues(title, icon);
}
if I do that I can't use any child widget to those widgets. Getting exception like: type 'Center' is not a subtype of type 'Widget6'. I copied this from flutter gallery I didn't find good documentation/guide. Sorry, I am a beginner.
Complete code below
class _MyHomePageState extends State<MyHomePage> {
List pageList = [
Widget1(),
Widget2(),
Widget3(),
Widget4(),
];
PageController _pageController;
int _selectedIndex = 0;
#override
void initState() {
_pageController = PageController(
initialPage: _selectedIndex,
);
super.initState();
}
void navigatePage(Widget widget) {
// problem is here
if (pageList.contains(widget)) {
_pageController.animateToPage(pageList.indexOf(widget, 0),
duration: Duration(milliseconds: 300), curve: Curves.ease);
}
else {
setState(() {
pageList.removeAt(_pageController.page.toInt());
pageList.insert(_pageController.page.toInt(), widget);
});
_pageController.animateToPage(_pageController.page.toInt(),
duration: Duration(milliseconds: 300), curve: Curves.ease);
}
Navigator.pop(context);
}
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: Drawer(
child: ListView(
children: <Widget>[
ListTile(
title: Text('Widget1'),
onTap: () => navigatePage(
Widget1(),
),
),
ListTile(
title: Text('Widget2'),
onTap: () => navigatePage(
Widget2(),
),
),
ListTile(
title: Text('Widget3'),
onTap: () => navigatePage(
Widget3(),
),
),
ListTile(
title: Text('Widget4'),
onTap: () => navigatePage(
Widget4(),
),
),
ListTile(
title: Text('Widget5'),
onTap: () => navigatePage(
Widget5(),
),
),
ListTile(
title: Text('Widget6'),
onTap: () => navigatePage(
Widget6(),
),
),
],
),
),
appBar: AppBar(
title: Text(widget.title),
),
body: PageView.builder(
onPageChanged: (newPage) {
setState(() {
this._selectedIndex = newPage;
});
},
controller: _pageController,
itemBuilder: (context, index) {
return Container(
child: pageList[index],
);
},
itemCount: pageList.length,
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
onTap: (index) => setState(() {
_selectedIndex = index;
_pageController.animateToPage(index,
duration: Duration(milliseconds: 300), curve: Curves.ease);
}),
items: pageList.map((page) {
return BottomNavigationBarItem(
backgroundColor: Colors.deepOrangeAccent,
icon: page.icon,
title: Text(page.title));
}).toList(),
),
);
}
}
Here List of dummy Widgets
class Widget1 extends StatelessWidget {
final String title = 'Widget1';
final Icon icon = Icon(Icons.school);
#override
Widget build(BuildContext context) {
return Center(
child: icon,
);
}
}
class Widget2 extends StatelessWidget {
// only title and icon are changed
}
class Widget3 extends StatelessWidget {
// only title and icon are changed
}
class Widget4 extends StatelessWidget {
// only title and icon are changed
}
class Widget5 extends StatelessWidget {
// only title and icon are changed
}
class Widget6 extends StatelessWidget {
// only title and icon are changed
}
Okay, I found the solution. And it has to do with operator== overriding
I missed this line if (runtimeType != other.runtimeType) return false;
The whole code stays the same.
#override
// ignore: hash_and_equals
bool operator ==(dynamic other) {
if (runtimeType != other.runtimeType) return false;
final Widget6 typedOther = other;
return title == typedOther.title;
}
#Ahmed Sorry for the late reply, I decided to put it in an answer rather than a comment.
One solution is yours, overriding == but I was thinking of using Key and then instead of using contains method, using something like:
if(myList.indexWhere((Widget widget)=> widget.key==_key) != -1)...
Suggestion
You can store icon and title as a map or a module instead of making 6 different Widget.
You can create another file, saying module.dart like this:
class Module {
final String title;
final Icon icon;
Module(this.title, this.icon);
#override
int get hashCode => hashValues(title.hashCode, icon.hashCode);
#override
bool operator ==(other) {
if (!identical(this, other)) {
return false;
}
return other is Module &&
this.title.compareTo(other.title) == 0 &&
this.icon == other.icon;
}
}
Then create another file that builds the page, saying mywidget.dart, like this:
class MyWidget extends StatelessWidget {
final Module module;
MyWidget({Key key,#required this.module}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: module.icon,
);
}
}
Then on each ListTile's onTap, Navigate like this:
...
ListTile(
title: Text('Widget1'),
onTap: () => navigatePage(
MyWidget(module: Module('Widget1', Icon(Icons.school)),)
),
),
...
So instead of storing Widgets, you store a Type(Here Module) that you declared.
You can also use the list's map to build each ListTile of the ListView for each Module, instead of doing it one by one. (if each item on the drawer are similar), Something like this:
List<Module> myTabs = [
Module('Widget1', Icon(Icons.school)),
Module('Widget2', Icon(Icons.home)),
];
...
Drawer(
child: ListView(
children:myTabs.map((Module module)=> ListTile(
title:Text( module.title),
onTap: navigatePage(MyWidget(module: module,)),
)).toList(),
) ,
);
...
I am learning app development on Flutter and can't get my Slider to work within the AlertDialog. It won't change it's value.
I did search the problem and came across this post on StackOverFlow:
Flutter - Why slider doesn't update in AlertDialog?
I read it and have kind of understood it. The accepted answer says that:
The problem is, dialogs are not built inside build method. They are on a different widget tree. So when the dialog creator updates, the dialog won't.
However I am not able to understand how exactly does it have to be implemented as not enough background code is provided.
This is what my current implementation looks like:
double _fontSize = 1.0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(qt.title),
actions: <Widget>[
IconButton(
icon: Icon(Icons.format_size),
onPressed: () {
getFontSize(context);
},
),
],
),
body: ListView.builder(
padding: EdgeInsets.symmetric(vertical: 15.0),
itemCount: 3,
itemBuilder: (context, index) {
if (index == 0) {
return _getListTile(qt.scripture, qt.reading);
} else if (index == 1) {
return _getListTile('Reflection:', qt.reflection);
} else {
return _getListTile('Prayer:', qt.prayer);
}
})
);
}
void getFontSize(BuildContext context) {
showDialog(context: context,builder: (context){
return AlertDialog(
title: Text("Font Size"),
content: Slider(
value: _fontSize,
min: 0,
max: 100,
divisions: 5,
onChanged: (value){
setState(() {
_fontSize = value;
});
},
),
actions: <Widget>[
RaisedButton(
child: Text("Done"),
onPressed: (){},
)
],
);
});
}
Widget parseLargeText(String text) {...}
Widget _getListTile(String title, String subtitle) {...}
I understand that I will need to make use of async and await and Future. But I am not able to understand how exactly. I've spent more than an hour on this problem and can't any more. Please forgive me if this question is stupid and noobish. But trust me, I tried my best.
Here is a minimal runnable example. Key points:
The dialog is a stateful widget that stores the current value in its State. This is important because dialogs are technically separate "pages" on your app, inserted higher up in the hierarchy
Navigator.pop(...) to close the dialog and return the result
Usage of async/await
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
double _fontSize = 20.0;
void _showFontSizePickerDialog() async {
// <-- note the async keyword here
// this will contain the result from Navigator.pop(context, result)
final selectedFontSize = await showDialog<double>(
context: context,
builder: (context) => FontSizePickerDialog(initialFontSize: _fontSize),
);
// execution of this code continues when the dialog was closed (popped)
// note that the result can also be null, so check it
// (back button or pressed outside of the dialog)
if (selectedFontSize != null) {
setState(() {
_fontSize = selectedFontSize;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Font Size: ${_fontSize}'),
RaisedButton(
onPressed: _showFontSizePickerDialog,
child: Text('Select Font Size'),
)
],
),
),
);
}
}
// move the dialog into it's own stateful widget.
// It's completely independent from your page
// this is good practice
class FontSizePickerDialog extends StatefulWidget {
/// initial selection for the slider
final double initialFontSize;
const FontSizePickerDialog({Key key, this.initialFontSize}) : super(key: key);
#override
_FontSizePickerDialogState createState() => _FontSizePickerDialogState();
}
class _FontSizePickerDialogState extends State<FontSizePickerDialog> {
/// current selection of the slider
double _fontSize;
#override
void initState() {
super.initState();
_fontSize = widget.initialFontSize;
}
#override
Widget build(BuildContext context) {
return AlertDialog(
title: Text('Font Size'),
content: Container(
child: Slider(
value: _fontSize,
min: 10,
max: 100,
divisions: 9,
onChanged: (value) {
setState(() {
_fontSize = value;
});
},
),
),
actions: <Widget>[
FlatButton(
onPressed: () {
// Use the second argument of Navigator.pop(...) to pass
// back a result to the page that opened the dialog
Navigator.pop(context, _fontSize);
},
child: Text('DONE'),
)
],
);
}
}
You just need to warp the AlertDialog() with a StatefulBuilder()