I receive some errors when I try to push my flutter page from neighbourhoodList to individual neighbourhoods (e.g. neighbourhoodlist_admirality).
In my neighbourhoodlist, I would like to navigate to the individual neighbourhood pages when the user has clicked on the relevant neighbourhood. As I have not build the individual neighbourhood pages yet, I have linked them to an example page i.e. NeighbourhoodAdmirality.
This is my code for the neighbourhoodlist page:
import 'package:flutter/material.dart';
import 'indv_neighbourhoods/neighbourhoodlist_admirality.dart';
class NeighbourhoodList extends StatefulWidget {
NeighbourhoodList({ this.name = "name"});
final String name;
#override
_NeighbourhoodListState createState() => _NeighbourhoodListState();
}
class _NeighbourhoodListState extends State<NeighbourhoodList> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Neighbourhoods',
),
),
body: _buildListView(context),
);
}
ListView _buildListView(BuildContext context){
return ListView.builder(
itemCount: allNeighbourhoods.length,
itemBuilder: (BuildContext content, int index) {
NeighbourhoodList neighbourhoodlist = allNeighbourhoods[index];
return NeighbourhoodListTile(neighbourhoodlist);
});
}
}
class NeighbourhoodListTile extends ListTile {
NeighbourhoodListTile(NeighbourhoodList neighbourhoodlist)
: super(
title: Text(neighbourhoodlist.name),
trailing: Icon(Icons.arrow_forward),
onTap: (){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => NeighbourhoodAdmiralty(neigh1)),
);
}
);
}
List<NeighbourhoodList> allNeighbourhoods = [
NeighbourhoodList(name: 'Admiralty'),
NeighbourhoodList(name: 'Aljunied'),
NeighbourhoodList(name: 'Ang Mo Kio'),
];
This is my code for an example page that I want to direct my neighbourhoodlist to when each individual neighbourhood is directed.
import 'package:flutter/material.dart';
class NeighbourhoodAdmiralty extends StatefulWidget {
final String neigh1;
NeighbourhoodAdmiralty(this.neigh1);
#override
_NeighbourhoodAdmiraltyState createState() => _NeighbourhoodAdmiraltyState();
}
class _NeighbourhoodAdmiraltyState extends State<NeighbourhoodAdmiralty> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Admiralty"),
),
body: Center(child: Text('This is the individual neighbourhood page'),
),
);
}
}
As I am still a beginner, I am facing some errors and have a few questions on these:
Error 1 on neighbourhoodlist.dart: "Undefined name 'context'" under Navigator.push --> not sure why this happens as I have already passed the BuildContext in my methods above
Error 2 on neighbourhoodlist.dart: "Undefined name 'neigh1'" under Navigator.push --> I would like to redirect the neighbourhoodlist.dart page to the individual neighbourhood sheets but I'm not sure what I pass here, I have tried 'neigh1' (my variable in neighbourhoodlist_admirality), 'name' - the variable in NeighbourhoodList, and 'index' 0 the variable inNeighbourhoodListState but none of them seem to work so far.
Appreciate all your help in resolving this and thanks in advance !
When you do
class NeighbourhoodListTile extends ListTile {
NeighbourhoodListTile(NeighbourhoodList neighbourhoodlist)
: super(
title: Text(neighbourhoodlist.name),
trailing: Icon(Icons.arrow_forward),
onTap: (){
Navigator.push(context, MaterialPageRoute(builder: (context) => NeighbourhoodAdmiralty(neigh1)));
}
);
}
you can't access context in onTap because Flutter hasn't yet provided one to you in this point. If you need the context, use a StatelessWidget instead, where you can access it in the build method:
class NeighbourhoodListTile extends StatelessWidget {
final NeighbourhoodList neighbourhoodlist;
const NeighbourhoodListTile(this.neighbourhoodlist);
#override
Widget build(BuildContext context) {
// Here's your context ^^^^^^^
return ListTile(
title: Text(neighbourhoodlist.name),
trailing: Icon(Icons.arrow_forward),
onTap: (){
Navigator.push(context, MaterialPageRoute(
builder: (context) => NeighbourhoodAdmiralty(neigh1)));
}
);
}
}
As to your second error, there is also no neigh1 variable at this point. I don't know what your logic is, but I think you want to replace it with neighbourhoodlist.name:
class NeighbourhoodListTile extends StatelessWidget {
final NeighbourhoodList neighbourhoodlist;
const NeighbourhoodListTile(this.neighbourhoodlist);
#override
Widget build(BuildContext context) {
return ListTile(
title: Text(neighbourhoodlist.name),
trailing: Icon(Icons.arrow_forward),
onTap: (){
Navigator.push(context, MaterialPageRoute(
builder: (context) => NeighbourhoodAdmiralty(neighbourhoodlist.name)));
}
);
}
}
Related
I'm new to UI Programming/ App development.
My goal is it to have a screen/page/whatever on which one can arrange different Widgets.
Of course this "layout" should be saved in some way but I have no clue how.
This is the page on which you can arrange the widgets:
const MyDesign({Key key,this.designName}):super(key: key);
final String designName;
#override
_MyDesignState createState() => _MyDesignState();
}
class _MyDesignState extends State<MyDesign> {
List<Widget> movableItems = [];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.designName),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
movableItems.add(MoveableStackItem(item: Knob(),));
});
}
),
body: Stack(
children:
movableItems,
),
);
}
}
As you can see I use a List to store my movable objects and a Stack to draw them.
Is there any possibility to store this List of widgets for a specific instance of this page and retrieve it after closing the app?
I also want to have the possibility to create multiple "layouts" heres my approach:
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<MyDesign> myDesignPages = [];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home'),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
final String dname = (await _asyncInputDialog(context)) as String;
setState(() {
myDesignPages.add(new MyDesign(designName: dname,));
});
},
child: Icon(Icons.add),
),
body:
new ListView.builder(
itemCount: myDesignPages.length,
itemBuilder: (BuildContext context, int i){
return ListTile(
title: Text(myDesignPages[i].designName),
onTap: () {
Navigator.push<dynamic>(
context,
MaterialPageRoute<dynamic>(
builder: (context) => myDesignPages[i]) );
},
);
}
),
);
}
}
I assume that I'm quite off the track here. For that I'm happy places like this exist.
Thanks a lot
To answer your first question: Yes it's possible to encode and save your list content as a String and to retrieve it later by decoding the saved String. To save String as key value pair you can for example use this plugin Shared preferences. To encode and decode your list you can use respectively jsonEncode and jsonDecode from dart:convert package
I have the follwing code with a _checkCreds function. I want to show an alert when that button is pressed.
When I replace the print() statement with an AlertDialog(), I get a "No MaterialLocalizations found".
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
void _checkCreds(bool val) {
if(!val){
print("Warning, show alert here instead of print!");
return;
}
...
// Continue executing
}
Widget build(BuildContext context) {
return MaterialApp(
home: RaisedButton(
child: "Press me to trigger an alert!" ),
onPressed: () => _checkCreds(false),
);
}
}
I've figured it out, coming for a React environment I didn't know where "BuildContext" fits in all of this. After further investigation I passed the current context as an argument to the function that calls the Alert.
Above main():
Future<void> _ackAlert(BuildContext context) {
return showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Error'),
content: const Text('Please enter a valid text'),
actions: <Widget>[
FlatButton(
child: Text('Okay'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
Inside the main widget:
Widget build(BuildContext context) {
return MaterialApp(
home: RaisedButton(
child: "Press me to trigger an alert!" ),
// I've added a parameter that takes the current context
onPressed: () => _checkCreds(false, context),
);
}
I think this is what you want:
void _checkCreds(bool val){//put it inside 'MyAppState' class
if(!val){
showDialog(
barrierDismissible: true,//tapping outside dialog will close the dialog if set 'true'
context: context,
builder: (context){
return Dialog(
//Add code here
);
}
);
}
...
// Continue executing
}
AlertDialog and Dialog has same properties except AlertDialog has content property whereas Dialog has child property. Both does same work.
import 'package:flutter/material.dart';
import 'fruits_listing_card.dart';
import 'fruits_page.dart';
Map<String, Widget> fruits = {
"banana": FruitsListingCards(
fruitBGColor: 0xFFF8A8B5,
fruitImagePath: 'images/fruits/banana.png',
fruitName: 'Banana',
fruitPrice: 'Rs. 105',
fruitShortDescription: 'Ripe & Tasty',
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => FruitsPage()),);
},
),
}
// Second File
import 'package:flutter/material.dart';
class FruitsPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Container(),
),
);
}
}
Both the code are in different files.
FruitsListingCards is a widget that has a Gesture detector functionality. onTap is a parameter that takes function.
I am using FruitsListingCards in the main file and whenever a user taps on it, should go to the FruitsPage screen. But the error is not letting me to do so. Any solution with proper explanantion will help me a lot.
EDIT:
For proper understanding of code, check my repo:
https://github.com/RaghavTheGreat1/fruits_delivery/tree/master/lib
You have to provide context some how, so that it can connect the last screen and next screen.
You can wrap inside a function for that.
Following minimal code will help you more to understand.
class DeleteWidget extends StatefulWidget {
#override
_DeleteWidgetState createState() => _DeleteWidgetState();
}
class FruitsPage extends StatelessWidget {
final Function call;
FruitsPage({this.call});
#override
Widget build(BuildContext context) {
return Container(
child: RaisedButton(
child: Text("press"),
onPressed: call,
),
);
}
}
class NewPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
child: Text("FruiysPage"),
),
),
);
}
}
callme(context) {
Map<String, Widget> fruits = {
"banana": FruitsPage(
call: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => NewPage()),
);
},
)
};
return fruits;
}
Well, I figured out the problem on omy own and it was easier than what other people had suggested me even if they haven't looked into my code committed in my GitHub repo.
The quick solution or fix was to add Navigator.push(context, MaterialPageRoute(builder: (context) => fruitsPage)); inside the anonymous function of GestureDetector in FruitsListingCards and then returning fruitsPage(which is a dynamic variable that will take Class object as parameter).
I just started learning Dart/Flutter, and I have been advised to use the Fluro package for navigation.
Is it possible to switch between 2 StatefulWidget "pages" with Fluro router?
I had no problems when switching between stateless widgets, but when I've tried to make them stateful, I got this error: "The return type 'Page2' isn't a 'Widget', as defined by anonymous closure." I can't figure out what I should change in the code.
class FluroRouter {
static Router router = Router();
static void setupRouter() {
router.define("Page2", handler: page2Handler);
router.define("Page1", handler: page1Handler);
}
static Handler page2Handler = Handler(
handlerFunc: (BuildContext context, Map<String, dynamic> params) => Page2());
static Handler page1Handler = Handler(
handlerFunc: (BuildContext context, Map<String, dynamic> params) => Page1());
}
class BasePage2 extends StatefulWidget {
#override
State createState() {
return Page2();
}
}
class Page2 extends State<BasePage2> {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome',
home: Scaffold(
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.pink,
child: Icon(Icons.router),
onPressed: () {
Navigator.pushNamed(context, 'Page1');
},
),
appBar: AppBar(
title: Text("Page 2"),
),
body: Center(
child: Text("Page 2"),
)),
);
}
}
//Page 1 looks the same, only the text says "Page 1"
Thank you in advance for any suggestions.
You need to pass the StatefulWidget and not the State:
... => BasePage2()
... => BasePage1()
I'm using Provider in my flutter app, and when I go to a new page, the data provided to the Provider at page 1 is not accessible in page 2.
The way I understood the way Provider works, was that there is a central place where one stores all the data, and one can access that data anywhere in the application. So in my application, which is shown below, ToDoListManager is the place where all the data is stored. And if I set the data in Page 1, then I will be able to access that data in Page 2, and vice versa.
If this is not correct, then what part is wrong? And why isn't it working in my application?
Here's the code
Page 1
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
builder: (context) => ToDoListManager(),
child: Scaffold(
appBar: AppBar(
title: Text('Cool Project'),
),
body:e ToDoList(),
),
);
}
}
class ToDoList extends StatelessWidget {
#override
Widget build(BuildContext context) {
final toDoListManager = Provider.of<ToDoListManager>(context);
return ListView.builder(
itemCount: toDoListManager.toDoList.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => Details(index)));
},
child: Text(toDoListManager.toDoList[index]),
);
},
);
}
}
Page 2
class Details extends StatelessWidget {
final int index;
Details(this.index);
#override
build(BuildContext context) {
return ChangeNotifierProvider(
builder: (context) => ToDoListManager(),
child: Scaffold(
appBar: AppBar(
title: Text('Details Bro'),
),
body: AppBody(index)),
);
}
}
class AppBody extends StatelessWidget {
final int index;
AppBody(this.index);
#override
Widget build(BuildContext context) {
final toDoListManager = Provider.of<ToDoListManager>(context);
print(toDoListManager.toDoList);
return Text(toDoListManager.toDoList[1]);
}
}
ToDoListProvider
class ToDoListManager with ChangeNotifier {
List<String> _toDoList = ['yo', 'bro'];
List<String> get toDoList => _toDoList;
set toDoList(List<String> newToDoList) {
_toDoList = newToDoList;
notifyListeners();
}
}
You have 2 options:
Place your ChangeNotifierProvider above your MaterialApp so that is accesible from any of you Navigator routes.
Keep your Home widget as is but when pushing the new widget with the Navigator provide the original Manager.
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return Provider<ToDoListManager>.value(
value: toDoListManager,
child: Details(index),
);
},
),
);
},
With both approaches you don't need to create a new ChangeNotifierProvider in your details screen.