Flutter IconTheme won't apply - flutter

I have a simple flutter app that will switch theme on button pressed, while the overall theme is applied some that I had defined before doesn't seem to be applied in my widget, like when I define default color of Icon to green like this:
final ThemeData themeDark = ThemeData.dark().copyWith(
iconTheme: IconThemeData(
color: Colors.green,
),
);
when I use it in my floating action button icon it still uses the default IconTheme
class SwitchThemeButton extends StatelessWidget {
SwitchThemeButton({Key? key, required this.onPressed}) : super(key: key);
final VoidCallback onPressed;
#override
Widget build(BuildContext context) {
return FloatingActionButton(
child: Icon(
Icons.favorite,
// This won't work unless I manually assign it!
// color: IconTheme.of(context).color,
),
onPressed: onPressed,
);
}
}
Can somebody point it up where I'm doing it wrong? This is how the full code looks like by the way.
Full code:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
final ThemeData themeDark = ThemeData.dark().copyWith(
iconTheme: IconThemeData(
color: Colors.green,
),
);
final ThemeData themeLight = ThemeData.light();
class MyApp extends StatefulWidget {
#override
MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
final _isDarkTheme = ValueNotifier<bool>(true);
#override
Widget build(BuildContext context) {
return ValueListenableBuilder<bool>(
valueListenable: _isDarkTheme,
builder: (context, bool isDark, _) => MaterialApp(
theme: themeLight,
darkTheme: themeDark,
themeMode: isDark ? ThemeMode.dark : ThemeMode.light,
home: Scaffold(
body: Stack(
children: [
Align(
alignment: Alignment.center,
child: MyWidget(),
),
Align(
alignment: Alignment.bottomRight,
child: SwitchThemeButton(onPressed: _switchTheme),
),
],
),
),
),
);
}
void _switchTheme() {
_isDarkTheme.value = !_isDarkTheme.value;
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Text('Make me green!', style: Theme.of(context).textTheme.headline4);
}
}
class SwitchThemeButton extends StatelessWidget {
SwitchThemeButton({Key? key, required this.onPressed}) : super(key: key);
final VoidCallback onPressed;
#override
Widget build(BuildContext context) {
return FloatingActionButton(
child: Icon(
Icons.favorite,
// color: IconTheme.of(context).color,
),
onPressed: onPressed,
);
}
}

i use this to change the icon color on floatButton
ThemeData lightTheme = ThemeData(
colorScheme: ThemeData().colorScheme.copyWith(
onSecondary : Colors.orange,
),);

The color of the Icon in theFloatingActionButton will be assigned based on Theme.of(context).accentColor.

Related

error on update the state of textStyle after upgrade to 3.7.0

after upgrade the flutter from 3.3.10 to 3.7.0 this error start appear after change the state of textStyle of Material widget:
Failed to interpolate TextStyles with different inherit values.
there is a simple exemple:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({
Key? key,
required this.title,
}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool state = false;
void _changeState() {
setState(() {
state = !state;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Material(
borderRadius: const BorderRadius.all(Radius.circular(20.0)),
elevation: 2,
textStyle: !state ? const TextStyle(color: Color(0xff4c4c4c)) : null,
child: const Text('Click me')
),
floatingActionButton: FloatingActionButton(
onPressed: _changeState,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
This is because, when you toggle the state of the textStyle property, it's going from null to a TextStyle object. Because null does not have any inherit property, it's trying to interpolate between two different inherit values, which is not possible.
Try to provide default text style for the Material widget so that it will always have a textstyle to inherit from.
Material(
borderRadius: const BorderRadius.all(Radius.circular(20.0)),
elevation: 2,
textStyle: !state && Theme.of(context).textTheme.bodyText1 != null
? Theme.of(context).textTheme.bodyMedium.copyWith(color: Color(0xff4c4c4c))
: Theme.of(context).textTheme.bodyMedium,
child: const Text('Click me')
);

Change Background Color of Elevated Button OnPressed in Flutter

I need to change background color of elevated button, on click to indicate it as selected. I have tried this.
class _MyState extends State<MyPage> {
bool _flag = true;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () => setState(() => _flag = !_flag),
child: Text(_flag ? 'Red' : 'Green'),
style: ElevatedButton.styleFrom(
backgroundColor: _flag ? Colors.red : Colors.teal,
),
),
),
);
}
}
Here on onPressed(): The color is not changing
On DartPad your code works Link to dartpad.
Be sure you implemented correctly the stateful widget:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({
Key? key,
}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _flag = true;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () => setState(() => _flag = !_flag),
child: Text(_flag ? 'Red' : 'Green'),
style: ElevatedButton.styleFrom(
backgroundColor: _flag ? Colors.red : Colors.teal,
),
),
),
);
}
}

Set StatefulBuilder state outside of StatefulBuilder widget

I'm trying to set a StatefulBuilder widget state outside of its widget. Most examples and the documentation available show the setState method being used inside the widget, however I'm wondering if such method can be called from outside of the StatefulBuilder widget. Here's an example:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'StackOverflow Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'StackOverflow Example'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return new GestureDetector(
//Change one of the icon's color using the tap down function
onTapDown: (TapDownDetails details) {
return changeColor(details);
},
child: Scaffold(
appBar: AppBar(
title: Text('Example'),
),
body: Center(
child: Column(children: [
//This widget should be rebuilt
StatefulBuilder(
builder: (BuildContext context, StateSetter setState)
{
Color _iconColor = Colors.black;
return Icon(
Icons.money,
size: 50,
color: _iconColor,
);
}
),
//This icon should not be rebuilt
Icon(
Icons.euro,
size: 50,
color: Colors.black,
),
]),
),
),
);
}
void changeColor(TapDownDetails details) {
//Rebuilt StatefulBuilder widget here, but how?
setState(() {
_iconColor = Colors.green;
});
}
}
Currently I get an error because of the _iconColor variable being used in setState. I am also aware that it may be impossible to access it outside of the widget. If that's the case, what would be a better solution to change the icon's color without resorting to rebuilding the whole StatefulWidget?
Thanks for your time.
You can use the ValueListenableBuilder widget.
Example:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'StackOverflow Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'StackOverflow Example'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
ValueNotifier _iconColor = ValueNotifier<Color>(Colors.black);
#override
Widget build(BuildContext context) {
return new GestureDetector(
//Change one of the icon's color using the tap down function
onTapDown: (TapDownDetails details) {
return changeColor(details);
},
child: Scaffold(
appBar: AppBar(
title: Text('Example'),
),
body: Center(
child: Column(children: [
//This widget should be rebuilt
ValueListenableBuilder(
valueListenable: _iconColor,
builder: (ctx, value, child) {
return Icon(
Icons.money,
size: 50,
color: value,
);
}
),
//This icon should not be rebuilt
Icon(
Icons.euro,
size: 50,
color: Colors.black,
),
]),
),
),
);
}
void changeColor(TapDownDetails details) =>
_iconColor.value = Colors.green
}
This is one way to achieve what you intend, if you have to definitely use the StatefulBuilder.
Basically we are storing the StateSetter that we receive from the StatefulBuilder.builder
class Sample extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return SampleState();
}
}
class SampleState extends State<Sample> {
StateSetter internalSetter;
Color color = Colors.black;
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(title: Text('Sample')),
body: Column(
children: [
GestureDetector(
onTap: () {
setState(() {
color = Colors.deepOrange;
});
},
child: Text('Press'),
),
StatefulBuilder(builder: (context, setter) {
internalSetter = setter;
return Container(
height: 100,
width: 100,
color: color,
);
}),
Undisturbed(),
],
),
);
}
}
class Undisturbed extends StatelessWidget {
#override
Widget build(BuildContext context) {
print("Undisturbed is built");
return Container(
width: 100,
height: 100,
color: Colors.red,
);
}
}

Conditional highlightColor on MaterialButton in flutter

I'm trying to build a quiz with multiple buttons. When the user presses the button for the right answer I want the button to have a highlightColor of green. If its the wrong button then the highlightColor is red. The issue I have is when I click a button the right highlightColor will show but you also get a glimpse of the wrong color. If I use the splashColor instead then you only see the right color which is good but the effect is not the same.
Here is a simplified version of the code with just one button but with the same issue.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State {
bool _winner = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Press the button:',
),
MaterialButton(
minWidth: MediaQuery.of(context).size.width * 0.90,
height: 60.0,
color: Colors.grey[800],
splashColor: Colors.transparent,
//splashColor: _winner ? Colors.green : Colors.red,
highlightColor: _winner ? Colors.green : Colors.red,
onPressed: () {
setState(() {
});
_winner ? _winner=false : _winner=true;
}
),
],
),
),
);
}
}
Thing here with the highlight fade animation that it last 200 milliseconds and the setState is called before the animation is over, thats why you get glimpse of the other color.
One solution is to setState after 200 milliseconds delay.
onPressed: () {
Future.delayed(Duration(milliseconds: 200), () {
setState(() {});
_winner ? _winner = false : _winner = true;
});
}),
another solution is to use GestureDetector with onTap event
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State {
bool _winner = true;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('widget.title'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Press the button:',
),
GestureDetector(
onTapDown: (_) {
setState(() {});
_winner ? _winner = false : _winner = true;
},
child: ColorButton(
isWinner: _winner,
),
),
],
),
),
);
}
}
class ColorButton extends StatefulWidget {
const ColorButton({
Key key,
#required this.isWinner,
}) : super(key: key);
final bool isWinner;
#override
_ColorButtonState createState() => _ColorButtonState();
}
class _ColorButtonState extends State<ColorButton> {
bool isPressed = false;
#override
Widget build(BuildContext context) {
return GestureDetector(
onTapDown: (_) {
setState(() {
isPressed = true;
});
},
onTapUp: (_) {
setState(() {
isPressed = false;
});
},
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
height: 60.0,
width: MediaQuery.of(context).size.width * 0.9,
color: isPressed
? widget.isWinner ? Colors.green : Colors.red
: Colors.grey[800],
),
);
}
}

How to have Stateful Widgets in tabBarView?

I had two Stateless Widgets in my Tab Bar View and my app was working fine. But then there was a requirement to make a button in one of the views and change the state of the button hence I made it as Stateful widgets but now the Tab Bar View doesn't accept it and gives the exception on running that ' type 'task' is not a subtype of type 'StatelessWidget''. Can we have only Stateless Widgets in TabBar views? How can i Fix this?
My main.dart file having the TabBarview
import 'package:flutter/material.dart';
import 'package:flutter_convertor/task.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: DefaultTabController(length: 2,child: MyHomePage(title: '')),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
Widget build(BuildContext context){
final list = ListView.builder(
itemBuilder: (context, position) {
//some other implementation
},
);
return Scaffold(
appBar: AppBar(
title: Text('Home'), bottom: TabBar(
tabs: [
Tab(icon: Icon(Icons.directions_car)),
Tab(icon: Icon(Icons.directions_transit)),
]),
),
body: TabBarView(children: [list, task()]));
}
}
My Stateful widget task
class task extends StatefulWidget{
#override
taskState createState() => new taskState();
}
class taskState extends State<task> {
int current_step = 0;
bool isButtonDisabled;
#override void initState() {
super.initState();
isButtonDisabled = false;
}
formReady(){
setState(() {
isButtonDisabled = !isButtonDisabled ;
});
}
#override
Widget build(BuildContext context) {
Column taskScreen = Column(
children: <Widget>[Expanded(
child: ListView(
children: <Widget>[
//other implementation
FlatButton(
color: Colors.red,
textColor: Colors.black,
shape: new RoundedRectangleBorder(borderRadius: new BorderRadius.circular(20.0)),
disabledColor: Color(0XFFf9c3c1),
disabledTextColor: Colors.white,
padding: EdgeInsets.all(8.0),
splashColor: Colors.red[400],
onPressed: isButtonDisabled ? null : _completePage
,
child: Text(
"Completed",
style: TextStyle(color: Colors.white, fontSize: 15.0, fontWeight: FontWeight.bold),
),
)
,
],
)
]);
return taskScreen;
}
}
You need to use the AutomaticKeepAliveClientMixin for this to work. It's a fairly simple mixin to use. You only need to include the
#override
bool get wantKeepAlive => true;
and add super.build(context) to your build method.
Your updated class would look something like this:
class task extends StatefulWidget{
#override
taskState createState() => new taskState();
}
class taskState extends State<task> with AutomaticKeepAliveClientMixin<task>
#override
Widget build(BuildContext context) {
super.build(context);
...
}
}