Custom theme not working properly. [Flutter] - dart

I have created the following theme for my app:
ThemeData _buildDarkTheme() {
final baseTheme = ThemeData(fontFamily: "Sunflower",);
return baseTheme.copyWith(
brightness: Brightness.dark,
primaryColor: Colors.grey[800],
accentColor: Colors.grey[850]);
}
I then apply it to my app as follows:
class MyApp extends StatelessWidget {
MyApp({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return new MaterialApp(
theme: _buildDarkTheme(),
home: new Scaffold(
appBar: _buildAppBar(),
body: new Container(
color: Theme.of(context).accentColor,
height: double.infinity,
child: new ListView.builder(...
However, when I try to access the accent color inside the container (or anywhere else) instead of it being the expected, Colors.grey[850], it instead defaults to blue. Also, trying to use the custom font Sunflower font family does not work, but when I instead use
new Text("Hello World", style: new TextStyle(fontFamily: "Sunflower"))
The font appears correctly.
I am new to flutter and dart so any help resolving these issues would be appreciated.

This is to do with how context and Theme.of work.
From the Theme class source code:
static ThemeData of(BuildContext context, { bool shadowThemeOnly = false }) {
final _InheritedTheme inheritedTheme =
context.inheritFromWidgetOfExactType(_InheritedTheme);
if (shadowThemeOnly) {
if (inheritedTheme == null || inheritedTheme.theme.isMaterialAppTheme)
return null;
return inheritedTheme.theme.data;
}
final ThemeData colorTheme = (inheritedTheme != null) ? inheritedTheme.theme.data : _kFallbackTheme;
final MaterialLocalizations localizations = MaterialLocalizations.of(context);
final TextTheme geometryTheme = localizations?.localTextGeometry ?? MaterialTextGeometry.englishLike;
return ThemeData.localize(colorTheme, geometryTheme);
}
Theme.of (and Navigator.of(), ....of() etc), look at the context you pass them and then iterate upwards through the tree of widgets looking for a widget of the type specified.
Now, looking at your code
Widget build(BuildContext context) {
return new MaterialApp(
theme: _buildDarkTheme(),
home: new Scaffold(
appBar: _buildAppBar(),
body: new Container(
color: Theme.of(context).accentColor,
you can see that the context you're passing into Theme.of is actually the context above the theme you're creating. So it won't find your theme and will revert to the default. This is because the widget tree looks somewhat like the following (ignoring all the intermediate layers, with the arrow pointing to the context you're using.
MyApp - context <--------
MaterialApp
Theme
Scaffold
There are two ways to fix this; the first is to use a Builder class to build your widget within a closure that has the context below the theme. That would look something like this:
class MyApp extends StatelessWidget {
MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: _buildDarkTheme(),
home: Scaffold(
appBar: _buildAppBar(),
body: Builder(
builder: (context) => Container(
color: Theme.of(context).accentColor,
height: double.infinity,
child: ListView.builder(...)
),
),
),
);
}
}
And it would make a tree that looks somewhat like this:
MyApp - context
MaterialApp
Theme
Scaffold
Builder - context <---------
The other (preferable) option is to split out the code for your builder into its own class - either a StatelessWidget-inherited class or a StatefulWidget and State pair.

I had the same problem using themes in the MaterialApp class. If you want to have only one theme for your entire Flutter app, you just have to use one theme in the main page (the one that calls all the other pages).
After doing that, in the other pages, never return the MaterialApp class, otherwise it will overwrite your theme and use the default one.
Here is the code for my main page:
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
fontFamily: "Test",
primaryColor: Colors.yellow,
buttonColor: Colors.yellow,
),
routes: {
'/': (BuildContext context) => AuthPage(),
},
);
After that, in the AuthPage, return Scaffold and not MaterialApp.

why wouldn't you make a _darkTheme variable like this:
ThemeData _darkTheme = _buildDarkTheme();
and then use it to define to color?
class MyApp extends StatelessWidget {
MyApp({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: _buildDarkTheme(),
home: Scaffold(
appBar: _buildAppBar(),
body: Container(
color: _darkTheme.accentColor,
height: double.infinity,
child: ListView.builder(...

Related

Default back button not showing after pushing new screen

I had pushed a new Screen from my main screen and it had navigated smoothly and also showed the back button however now that I run the button, the default button is not showing in my code.
The only change that I made was that I changed the route in my main file from taskScreen() to task().
But this was essential as i wanted to make some more routes from my second screen and so it had to return a Material App.
How can i get the default back button back?
My code:
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',
home: DefaultTabController(length: 2,child: MyHomePage(title: '')),
routes: <String, WidgetBuilder>{
"/TaskScreen": (BuildContext context) => new task(), //CHANGED HERE
},
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
#override
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(
..
),
body: ...
}
}
My second screen
class task extends StatelessWidget{
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Task',
home: new taskScreen(),
routes: <String, WidgetBuilder>{
"/Completed": (BuildContext context) => new Completed()
}
);
}
}
class taskScreen extends StatefulWidget{
#override
taskState createState() => new taskState();
}
class taskState extends State<taskScreen> {
#override
Widget build(BuildContext context) {
Column taskScreen = Column (...)
return Scaffold(
appBar: AppBar(
title: Text('Task Screen')),
body: taskScreen,
);
}
}
Class name must to start with uppercase character. You have wrong structure, MaterialApp must to be only one time.
https://dart.dev/guides/language/language-tour
For me it happened when I added a draw to all my various AppBars ... quick googling provided a solution which worked for me:
I added to the AppBar the following which let flutter take a conscious decision whether there is or is not what to go back to ...
automaticallyImplyLeading: true,
Then I added defined how the "leading" should look like (also to the appbar):
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context, false),
),
Making a long story short: Adding the following to the respective AppBar did the work:
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context, false),
),
Remove the second routes MaterialApp and replace it with an AppBar

How to prevent Flutter app from scrolling to top after calling setState?

I have an ExpansionPanelList inside a SingleChildScrollView. Whenever I (un)fold a panel and therefore call setState, the SingleChildScrollView scrolls back to the top. How can I prevent this?
#override
Widget build(BuildContext context) {
final _scaffoldKey = new GlobalKey<ScaffoldState>();
return new Scaffold(
key: _scaffoldKey,
appBar: new AppBar(
title: new Text(widget.title),
),
body: new SingleChildScrollView(
child: new ExpansionPanelList(
children: <ExpansionPanel>[
// panels
],
expansionCallback: (int index, bool isExpanded) {
setState(() {
// toggle expanded
});
},
), // ExpansionPanelList
), // SingleChildScrollView
); // Scaffold
}
This answer suggests using a custom ScrollController with keepScrollOffset set to true, however this is the default value and setting it explicitly to true therefore does not change anything.
That's because you are using a new Key every time you rebuild the widget (setState).
To fix your issue just move the code below outside the build method
final _scaffoldKey = new GlobalKey<ScaffoldState>();
Like this :
final _scaffoldKey = new GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
return new Scaffold(
key: _scaffoldKey,
appBar: new AppBar(
title: new Text(widget.title),
),
I was having the same problem, and somehow the answer from #diegoveloper did not do the trick for me.
What i ended up doing was separating the SingleChildScrollView in an independent StatefulWidget: That also fixed the scroll animation.
My code then ended up being something like
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: MyExpansionList(),
...
class MyExpansionListextends StatefulWidget {
#override
_MyExpansionListState createState() => _MyExpansionListState();
}
class _MyExpansionListState extends State<MyExpansionList> {
#override
Widget build(BuildContext context) {
return Scrollbar(
isAlwaysShown: true,
showTrackOnHover: true,
child: SingleChildScrollView(
child: Column(
children: [
ExpansionPanelList(animationDuration: Duration(seconds: 1),
In this way the setState() did update only the ExpansionPanelList/ScrollView and not the whole Scaffold.
I hope this also helps others facing same problem...
Nothing helped me until I realised that in the build() function I was calling jumpTo() of the controller that was attached to my list. Like this:
#override
Widget build(BuildContext context) {
if (widget.controller.hasClients) {
widget.controller.jumpTo(0);
}
...
}
I removed these lines and the problem was gone. Happy coding :)
You have to pass a key to the SingleChildScrollView. Otherwise, its state is renewed every setState call.
final _scrollKey = GlobalKey();
SingleChildScrollView(
key: _scrollKey,
child:

Calling an instance of a stateless class's build vs a stateful class's build

I have noticed a difference when calling a stateful class compared to a stateless one.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Material App Title',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: "Title"
),
body: ListView(
children: [
Container(
height: 300.0,
child: ItemListState()._buildItemsList(),
)
],
)),
);
}
}
In the example above if the class is stateful(ItemListState()) I need to call ItemListState().buildItemsList() (the method returns a ListView I have built.)
If the class is stateless I can just call ItemListState()(the build method returns the same ListView)
Why is it acceptable to use what's returned from build from the stateless class but not the stateful one. I suspect it's because the framework cannot be sure when to call build or something similar.
The get an error when trying to use the build function of the stateful class to return my listView The argument type 'ItemListState' can't be assigned to the parameter type 'Widget'
There is no difference when you call Stateful or Stateless Widget , both classes have build method so in both cases you have to use the constructor of the Stateful or Stateless not the constructor of the State class.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Material App Title',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: "Title"
),
body: ListView(
children: [
Container(
height: 300.0,
child: ItemList(),
)
],
)),
);
}
}
If you are using Stateful
class ItemList extends StatefulWidget {
#override
ItemListState createState() => ItemListState();
}
class ItemListState extends State<ItemList> {
_buildItemsList(){
return Container();
}
#override
Widget build(BuildContext context) {
return _buildItemsList();
}
}
If you are using Stateless
class ItemList extends StatelessWidget {
_buildItemsList(){
return Container();
}
#override
Widget build(BuildContext context) {
return _buildItemsList();
}
}

Flutter: Theme not applied to Text Widget

I am trying to make a custom theme that is applied to only the children of that theme.
However when I run the app, the Text widget that displays "hello" is still blue. I want to make it yellow.
Can anyone show me where i'm going wrong?
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
brightness: Brightness.dark,
primaryColor: Colors.lightBlue[800],
accentColor: Colors.cyan[600],
textTheme: TextTheme(body1: TextStyle(color: Colors.blue))),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: Text(widget.title),
),
body: Center(
child: Theme(
data: Theme.of(context).copyWith(
textTheme:
TextTheme(body1: TextStyle(color: Colors.yellow))),
child: Text("hello"))));
}
}
To theme a Text you need to assign the value to style property
Text("Hello", style: Theme.of(context).textTheme.body1)
Make sure to use the correct context when doing Theme.of(context). You need a context that is a child of your new Theme.
You'll need to do the following :
Theme(
child: Builder(
builder: (context) {
return Text("Hello", style: Theme.of(context).textTheme.body1);
}
)
)
This is another way, because sometimes it's more convenient to override the default style
Text widget:
If the style argument is null, the text will use the style from the
closest enclosing DefaultTextStyle.
#override
Widget build(BuildContext context) {
final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context);
TextStyle effectiveTextStyle = style;
if (style == null || style.inherit)
effectiveTextStyle = defaultTextStyle.style.merge(style);
So, if you want to override the default style for the Text widget (when you do not pass the style property), you need to use the DefaultTextStyle widget
return new Scaffold(
appBar: new AppBar(
title: Text(widget.title),
),
body: Center(
child: DefaultTextStyle(
style: Theme.of(context).textTheme.body1.copyWith(color: Colors.yellow),
child: Text("hello"))
)
);
MaterialApp uses its TextStyle as its DefaultTextStyle to encourage developers to be intentional about their DefaultTextStyle
Latest Flutter Release mark body1 has deprecated, so now we can use bodyText2:
Text("My Text", style: Theme.of(context).textTheme.bodyText2)
New params(Right ones are new)
body2 => bodyText1;
body1 => bodyText2;

How do I set the background color of my main screen in Flutter?

I'm learning Flutter, and I'm starting from the very basics. I'm not using MaterialApp. What's a good way to set the background color of the whole screen?
Here's what I have so far:
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new Center(child: new Text("Hello, World!"));
}
}
Some of my questions are:
What's a basic way to set the background color?
What exactly am I looking at, on the screen? Which code "is" the background? Is there a thing to set the background color on? If not, what's a simple and appropriate "simple background" (in order to paint a background color).
Thanks for the help!
The code above generates a black screen with white text:
You can set background color to All Scaffolds in application at once.
Just set scaffoldBackgroundColor: in ThemeData:
MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(scaffoldBackgroundColor: const Color(0xFFEFEFEF)),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
I think you can also use a scaffold to do the white background. Here's some piece of code that may help.
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Testing',
home: new Scaffold(
//Here you can set what ever background color you need.
backgroundColor: Colors.white,
),
);
}
}
Here's one way that I found to do it. I don't know if there are better ways, or what the trade-offs are.
Container "tries to be as big as possible", according to https://flutter.io/layout/. Also, Container can take a decoration, which can be a BoxDecoration, which can have a color (which, is the background color).
Here's a sample that does indeed fill the screen with red, and puts "Hello, World!" into the center:
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new Container(
decoration: new BoxDecoration(color: Colors.red),
child: new Center(
child: new Text("Hello, World!"),
),
);
}
}
Note, the Container is returned by the MyApp build(). The Container has a decoration and a child, which is the centered text.
See it in action here:
There are many ways of doing it, I am listing few here.
Using backgroundColor
Scaffold(
backgroundColor: Colors.black,
body: Center(...),
)
Using Container in SizedBox.expand
Scaffold(
body: SizedBox.expand(
child: Container(
color: Colors.black,
child: Center(...)
),
),
)
Using Theme
Theme(
data: Theme.of(context).copyWith(scaffoldBackgroundColor: Colors.black),
child: Scaffold(
body: Center(...),
),
)
You should return Scaffold widget and add your widget inside Scaffold
Such as this code:
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Center(child: new Text("Hello, World!"));
);
}
}
Scaffold(
backgroundColor: Constants.defaulBackground,
body: new Container(
child: Center(yourtext)
)
)
It's another approach to change the color of background:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: Scaffold(backgroundColor: Colors.pink,),);
}
}
On the basic example of Flutter you can set with backgroundColor: Colors.X of Scaffold
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
backgroundColor: Colors.blue,
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add_circle),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
I think you need to use MaterialApp widget and use theme and set primarySwatch with color that you want. look like below code,
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
home: Scaffold(
backgroundColor: Color(
0xBF453F3F),
and done :)
You can just put the six digit hexa after (0xFF**......**):
return Scaffold(
backgroundColor: const Color(0xFFE9ECEF),
.....) } )
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Your App',
theme: ThemeData(
scaffoldBackgroundColor: Colors.black,
),
home HomeScreen(),
);
}
}
Sample code:
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Sample App'),
backgroundColor: Colors.amber, // changing Appbar back color
),
backgroundColor: Colors.blue, // changing body back color
),
),
);
}
As sirelon suggested, add scaffold color in the theme like this,
theme: new ThemeData(scaffoldBackgroundColor: const Color(0xFFEFEFEF)),
or can give color to individual scaffold like this
Scaffold(
backgroundColor: Color(0xFFF1F1F1),
...
);
Try the following code:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
scaffoldBackgroundColor: Colors.white, // Change the background color of all Scaffold widgets of your app here
),
home: const Scaffold(
body: Center(child: Text("Hello, World!")),
backgroundColor: Colors.white, // Change the background color of this Scaffold widget here
),
);
}
}

Resources