error when I use raisedButton in a widget - flutter

I am writing my first flutter app and i created two classes.
Those two classes represent two pages that i wish to navigate between and I am facing an issue with the "raisedButton".
The first class has 2 widgets and the button only works in one of them! whenever I try to cut+ paste the button to the desired widget => I get the following error:
The method 'findAncestorStateOfType' was called on null.
Receiver: null
Tried calling: findAncestorStateOfType()
I don't know what they mean by that.
Any help is appreciated.
class FirstRoute extends StatelessWidget {
Widget _contactList(String name, String image,[BuildContext context]) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.red,
backgroundImage: AssetImage(image),
radius: 35.0,
child: Text(nameInitial,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20.0)),
),
title: Text(name),
trailing: RaisedButton(
textColor: Colors.white,
color: Colors.blue,
child: Text('Details'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Details()),
);
},
),
),
);
}

you can use a named route by calling the Navigator.pushNamed(context,'/home'); Named routes allow for parameter passing '/customer/:${customer.id}'. the routing paths are defined onGenerateRoute attribute assigned to routing information
class _AppWidgetState extends State<AppWidget>{
bool _bright=false;
_brightnessCallback().
{setState(()=>_bright=!_bright);debugPrint("brightnessCallback fired");}
#override
Widget build(BuildContext context){
return MaterialApp(
title: "Flutter demo",
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHome2Widget(),
onGenerateRoute: handleRoute
);
}}
Route<dynamic> handleRoute(RouteSettings routeSettings)
{
List<String> nameParam= routeSettings.name.split(":");
String name=nameParam[0];
//int id=int.tryParse(nameParam[1]);
Widget childWidget;
if(name=="/Home")
{
childWidget=MyHome2Widget();
}else if(name=="/Summary")
{
childWidget=SummaryWidget();
}
return MaterialPageRoute(
builder:(context)=>childWidget
);
}

Related

Apply ButtonStyle Properties to All Buttons Globally

I've implemented several long ListViews containing ElevatedButtons in my flutter project, and would now like to apply various ButtonStyle properties to all of them. I am certain there is a way to avoid applying these properties to each button individually, but haven't been able to find anything in the Flutter docs or on Stack Overflow. Can this task be done globally?
Currently buttons have basic styling:
ElevatedButton(
style: const ButtonStyle(),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => authorA()),
);
},
child: const Text("Author A")),
ElevatedButton(
style: const ButtonStyle(),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => AuthorB()),
);
},
child: const Text("Author B")),
ElevatedButton(
style: const ButtonStyle(),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => AuthorC()),
);
},
child: const Text("Author C)")),
My ListView contains hundreds of these buttons, and there are several other long ListViews containing buttons that I would like to style as well. what if I wanted to modify the ButtonStyle() property for all of them like below:
ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(Colors.green),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => AuthorA()),
);
},
child: const Text("Author A")),
Use theme or darkTheme property of MaterialApp to set global styles.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
primary: Colors.green,
),
),
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
main.dart
MaterialApp(
theme: ThemeData(
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
primary: Colors.orange,
),
),
),
);
A ButtonStyle that overrides the default appearance of
ElevatedButtons when it's used with ElevatedButtonTheme or with the
overall Theme's ThemeData.elevatedButtonTheme.
The style's properties override ElevatedButton's default style, i.e.
the ButtonStyle returned by ElevatedButton.defaultStyleOf. Only the
style's non-null property values or resolved non-null
MaterialStateProperty values are used.
MaterialApp(
theme: ThemeData(
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
primary: Colors.orange,
),
),
),
);
Using the same properties add different styles as you want this is possible using .. Operator.
ElevatedButton(
style: const ButtonStyle()..,//Just Add .. here and add new color and properties
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => authorA()),
);
},
child: const Text("Author A")),
use -> MaterialApp -> theme -> elevatedButtonTheme -> style -> with styleForm -> primary : #Color

Method setState() is not updating the UI (Flutter)

My home screen is a Scaffold with a ListView at its body and a floating action button at the bottom. The action button takes the user to a second screen, where he can type a text into a text input and press save. The save button calls a method at the home screen that adds the text to the List variable over which ListView is based. The problem is: the List variable is being updated (I can see on the log), but the setState is not updating the ListView. What am I doing wrong?
Here's the code from the Home Screen:
import 'package:flutter/material.dart';
import 'addCounter.dart';
class Home extends StatefulWidget {
#override
HomeState createState() => HomeState();
}
class HomeState extends State<Home> {
List lista = <Widget>[];
void incrementLista(newItem) {
print('$lista');
setState(() {
lista.add(Box('newItem'));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('List of Counters'),
backgroundColor: Colors.deepPurple[1000],
),
body: Builder(
builder: (context)=>
Center(
child: ListView(children: lista),
),),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.grey[1000],
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddCounter(f: incrementLista)));
},
child: Icon(Icons.add, color: Colors.white)),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: BottomNavigationBar(
unselectedItemColor: Colors.grey[700],
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.list),
title: Text('Lista'),
),
BottomNavigationBarItem(
icon: Icon(Icons.insert_chart),
title: Text('Gráfico'),
),
],
selectedItemColor: Colors.blue,
),
);
}
}
And here is the code from the addCounter.dart:
import 'package:flutter/material.dart';
class AddCounter extends StatefulWidget {
final Function f;
AddCounter({#required this.f});
#override
_AddCounterState createState() => _AddCounterState();
}
class _AddCounterState extends State<AddCounter> {
final myController = TextEditingController();
#override
void dispose() {
myController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add a counter'),
backgroundColor: Colors.blue,
),
body: Column(children: [
Padding(
padding: EdgeInsets.all(15),
child: TextField(
controller: myController,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue, width: 2)),
hintText: 'Type a name for the counter'),
),
),
RaisedButton(
color: Colors.green,
onPressed: () {
widget.f(myController.text);
Navigator.pop(context);
},
child: Text(
'Save',
style: TextStyle(color: Colors.white),
),
)
]));
}
}
I don't think the code for the Box widget is relevant. It is basically a card with a title.
#pskink gave me an answer that worked perfectly. Here it is:
basically you should not use such a list of widgets, data and presentation layers should be separated, instead you should use a list of data only, see https://flutter.dev/docs/cookbook/gestures/dismissible for more info

Flutter - How can I change the background color of LicensePage?

I'd like to set the background colour of every screen except LicensePage to some colour, so I've specified the scaffoldBackbroundColor via the theme argument of MaterialApp as follows.
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(scaffoldBackgroundColor: Colors.blue.shade200),
home: HomeScreen(),
);
}
}
This changes the background colour of the licences page too, so in order to change it back to white, I tried overriding scaffoldBackbroundColor, but it did not work.
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Theme(
data: Theme.of(context).copyWith(scaffoldBackgroundColor: Colors.white),
child: Center(
child: RaisedButton(
child: const Text('Show licenses'),
onPressed: () => showLicensePage(context: context),
),
),
),
);
}
}
How can I do it?
In my case I found ThemeData(cardColor) was dictating the LicensePage background color. So,
showLicense(BuildContext context) {
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (context) => Theme(
data: ThemeData(
cardColor: Colors.yellow,
),
child: LicensePage(
applicationVersion: '0.1',
applicationIcon: Icon(Icons.person),
applicationLegalese: 'Legal stuff',
),
),
),
);
}
I came up with this and it worked successfully.
Scaffold(
body: Center(
child: RaisedButton(
child: const Text('Show licenses'),
onPressed: () => Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (context) => Theme(
data: Theme.of(context).copyWith(
scaffoldBackgroundColor: Colors.white,
),
child: LicensePage(...),
),
),
),
),
),
)
This way you cannot set the theme, set color using Container
Scaffold(
body: Container(
color: Colors.white,
child: Center(
child: RaisedButton(
child: const Text('Show licenses'),
onPressed: () => showLicensePage(context: context),
),
),
),
),

How to display SnackBar in Flutter?

I want to display a SnackBar in my Flutter app. I have read the docs and copyed it:
The body of my scaffold:
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text("Osztályok"),
leading: Padding(
padding: const EdgeInsets.only(left: 5.0),
child: IconButton(
icon: Icon(Icons.exit_to_app, color: Colors.white70),
onPressed: () {
authService.signOut();
authService.loggedIn = false;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => GoogleSignUp()));
})),
actions: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 5.0),
child: Row(
children: <Widget>[
IconButton(
icon: Icon(Icons.add_circle_outline,
color: Colors.white70),
onPressed: () {
createPopup(context);
}),
// IconButton(
// icon: Icon(Icons.search, color: Colors.black38),
// onPressed: null),
],
)),
],
),
The SnackBarPage class:
class SnackBarPage extends StatelessWidget {
void jelszopress(TextEditingController jelszoController, BuildContext context) async{
var jelszo;
DocumentReference docRef =
Firestore.instance.collection('classrooms').document(globals.getid());
await docRef.get().then((value) => jelszo= (value.data['Jelszo']) );
if (jelszo == jelszoController.text.toString()){
Navigator.push(context,
MaterialPageRoute(builder: (context) => InClassRoom()));
}
else{
Navigator.pop(context);
final snackBar = SnackBar(content: Text('Yay! A SnackBar!'));
Scaffold.of(context).showSnackBar(snackBar);
}
}
Future<String> jelszoba(BuildContext context) {
TextEditingController jelszoController = TextEditingController();
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Add meg a jelszót'),
content: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: TextField(
controller: jelszoController,
decoration: InputDecoration(hintText: "Jelszó")
)
),
actions: <Widget>[
MaterialButton(
elevation: 5.0,
child: Text('Mehet'),
onPressed: () {
jelszopress(jelszoController, context);
},
)]);
}
);
}
var nevek;
var IDS;
SnackBarPage(this.nevek, this.IDS);
#override
Widget build(BuildContext context){
return ListView.builder(
itemCount: nevek.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
onTap: () {
globals.setid(IDS[index]);
jelszoba(context);
},
title: Text(nevek[index]),
),
);
},
) ;
}
}
But my cody doesn't display the SnackBar. I tried the solution of this question: How to properly display a Snackbar in Flutter? but adding a Builder widget didn't help.
"Scaffold.of(context)" has been deprecated, will return null. Now use "ScaffoldMessenger.of(context)". As per Flutter documentation.
#override
Widget build(BuildContext context) {
// here, Scaffold.of(context) returns null
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: const Text('snack'),
duration: const Duration(seconds: 1),
action: SnackBarAction(
label: 'ACTION',
onPressed: () { },
),
));
},
child: const Text('SHOW SNACK'),
),
),
);
}
NOTE: Make sure your main.dart overrided build() function should return "MaterialApp" as a widget, such as:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
// Must be MaterialApp widget for ScaffoldMessenger support.
return MaterialApp(
title: 'My App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyDashboard(),
);
}
}
So based on the error, it would seem that the context passed in Snackbar.of() is not the correct context. This would make sense based on 1 & 2; and summary copied below:
Each widget has its own BuildContext, which becomes the parent of the widget returned by the StatelessWidget.build or State.build function. (And similarly, the parent of any children for RenderObjectWidgets.)
In particular, this means that within a build method, the build context of the widget of the build method is not the same as the build context of the widgets returned by that build method.
So this means that the build context you are passing in jelszoba(context) function is not the build context you need and is actually the build context of the widget that is instantiating the Scaffold.
So How to Fix:
To fix this wrap your Card widget in your SnackbarPage in a Builder widget and pass the context from it, to the jelszoba(context) method.
An example from 1 I post below:
#override
Widget build(BuildContext context) {
// here, Scaffold.of(context) returns null
return Scaffold(
appBar: AppBar(title: Text('Demo')),
body: Builder(
builder: (BuildContext context) {
return FlatButton(
child: Text('BUTTON'),
onPressed: () {
// here, Scaffold.of(context) returns the locally created Scaffold
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('Hello.')
));
}
);
}
)
);
}
You can normally use snack bar in the Bottom Navigation bar in this way. However, if you want to show it in the body, then just copy the code from Builder and paste it in the body of the scaffold.
Scaffold(bottomNavigationBar: Builder(builder: (context) => Container(child: Row(children: <Widget>[
Icon(Icons.add_alarm), Icon(Icons.map), IconButton(icon: Icon(Icons.bookmark),
onPressed:() {
Scaffold.of(context).showSnackBar(mySnackBar);
final mySnackBar = SnackBar(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
behavior: SnackBarBehavior.floating,
backgroundColor: Colors.white, duration: Duration(seconds: 1),
content: Text(
'Article has been removed from bookmarks',
),);
}
),
],
),
),
),
);
Note: In the behaviour property of SnackBar, you can just leave it empty. But the problem with that is "If you have Curved Navigation Bar or you have a floating action button above the bottom navigation bar, then the snackbar will lift these icons (or FAB ) and will affect the UI". That's why SnackBar.floating is more preferred as it is more capatible with the UI.
But you can check and see on your own which suits you the best.

How to give a height to the PopUpMenuButton in Flutter?

I am trying to create aPopupMenuButton.I have used the PopupMenuButton class.
PopupMenuButton(
padding: EdgeInsets.only(right: 8.0),
offset: Offset(-16, 0),
child: Container(
decoration: BoxDecoration(
color: Colors.orange,
borderRadius: BorderRadius.all(
Radius.circular(16.0),
)),
padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 12.0),
child: Text(
"Category",
style: TextStyle(color: Colors.white),
),
),
itemBuilder: (_) => <PopupMenuItem<String>>[
new PopupMenuItem<String>(
//I want this context to be scrollable with some fixed height on the screen
child: Row(
children: <Widget>[
Icon(Icons.arrow_right),
Text("Dairy & Bakery")
],
),
value: '1'),
],
)
I have tried implementing the PreferredSizeWidget but is not working on PopupMenuButton.
Edit: i meant fixed height :S
PopUpMenuButton does not support a fixed height. But what u can do is adjust the PopUpMenu Package.
Something similar is done
here
with the DropdownButton. For the PopUpMenu the implemenatition should work analogously, as both have the same strucktur. (Route, RouteLayout and PopUpMenu)
Edit:
You take a look at the the original code of the DropdownButton and then look at the changes the person did to it in the custom edition.
Then you take the code of the PopUpMenuButton and copy them into your own project and adjust them like it was done with the DropDownButton.
Then you use ure custom version of the PopUpMenuButton with the argument height.
Edit 2:
As you had some problems doing what I meant, I did it for you:
Just copy this file into your directory and import it into your code.
Then use CustomPopupMenuButton with a height instead of the original one.
Usage:
import 'package:flutter/material.dart';
import 'custom_popup_menu_button.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Home(),
);
}
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
enum WhyFarther { harder, smarter, selfStarter, tradingCharter }
class _HomeState extends State<Home> {
WhyFarther _selection;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'it does work here',
style: TextStyle(fontSize: 20),
),
),
body: Center(
child: CustomPopupMenuButton<WhyFarther>(
onSelected: (WhyFarther result) {
setState(() {
_selection = result;
});
},
height: 100,
itemBuilder: (BuildContext context) => <PopupMenuEntry<WhyFarther>>[
const PopupMenuItem<WhyFarther>(
value: WhyFarther.harder,
child: Text('Working a lot harder'),
),
const PopupMenuItem<WhyFarther>(
value: WhyFarther.smarter,
child: Text('Being a lot smarter'),
),
const PopupMenuItem<WhyFarther>(
value: WhyFarther.selfStarter,
child: Text('Being a self-starter'),
),
const PopupMenuItem<WhyFarther>(
value: WhyFarther.tradingCharter,
child: Text('Placed in charge of trading charter'),
),
],
)),
);
}
}
If anything is not working feel free to ask, perhaps I will look into it later.