I am trying to implement a favorite Button in Flutter. I am happy with the properties when clicking the button but I am not happy with the hover animation.
I want that only the icon color changes on a hover. I don't like the Bubble around the IconButton. Here is my approach:
MouseRegion FavoriteButton(int index) {
bool _favoriteHover = false;
return MouseRegion(
onHover: (e) {
setState(() {
_favoriteHover = true;
});
},
child: IconButton(
icon: Icon(
_projects[index].favorite
? Icons.favorite
: Icons.favorite_border_outlined,
color: _favoriteHover ? Colors.yellow : Colors.white,
),
onPressed: () {
setState(() {
_projects[index].favorite
? _projects[index].favorite = false
: _projects[index].favorite = true;
// TODO: Save Favorite state to config
});
},
color: Colors.transparent,
),
);
Unfortunately, the icon color does not change on hover.
Someone can help?
You try:
Don't hover: https://i.stack.imgur.com/oxGUh.png
Hover:https://i.stack.imgur.com/UyW2j.png
/// Flutter code sample for MouseRegion
// This example makes a [Container] react to being entered by a mouse
// pointer, showing a count of the number of entries and exits.
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
void main() => runApp(const MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const Center(
child: MyStatefulWidget(),
),
),
);
}
}
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
bool _favorite = false;
void _incrementEnter(PointerEvent details) {
setState(() {
_favorite = true;
});
}
void _incrementExit(PointerEvent details) {
setState(() {
_favorite = false;
});
}
Icon setIcon(){
if(_favorite){
return Icon(Icons.favorite);
}
return Icon(Icons.favorite_border_outlined);
}
Color setColor(){
if(_favorite){
return Colors.yellow;
}
return Colors.black;
}
#override
Widget build(BuildContext context) {
return Container(
child: MouseRegion(
onEnter: _incrementEnter,
onExit: _incrementExit,
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
IconButton(
icon: setIcon(),
color: setColor(),
onPressed: (){},
),
],
),
),
),
);
}
}
[1]: https://i.stack.imgur.com/UyW2j.png
Related
I'm trying to call a StatefulWidget(i.e FirstPage()) within a MaterialApp. I'm pretty much new to flutter and I don't know where I went wrong. According to my knownledge I've used StatefulWidget to tell flutter my screen on that page is going to encounter some changes in UI. But I got no idea to fix this error.
main.dart file:
import 'package:flutter/material.dart';
import 'package:flutter_project/main.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: FirstPage());
}
}
class FirstPage extends StatefulWidget {
const FirstPage({Key? key}) : super(key: key);
#override
State<FirstPage> createState() => _FirstPageState();
}
class _FirstPageState extends State<FirstPage> {
#override
Widget build(BuildContext context) {
String buttonName = "Click";
int currentIndex = 0;
return Scaffold(
appBar: AppBar(
title: const Text("App title "),
),
body: Center(
child: currentIndex == 0
? Container(
width: double.infinity,
height: double.infinity,
color: Colors.blueAccent,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: 280,
height: 80,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
side: BorderSide.none,
borderRadius: BorderRadius.circular(18),
),
backgroundColor: const Color.fromRGBO(9, 8, 99, 90),
foregroundColor: Colors.red,
),
onPressed: () {
setState(() {
buttonName = "Clicked";
//print(buttonName0);
});
},
child: Text(buttonName),
),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
//'BuildContext' - datatype and 'context' - variable name
return const SecondPage();
},
),
);
},
child: const Text("Move to new page"),
),
],
),
)
: Image.asset("images/img.png"),
),
bottomNavigationBar: BottomNavigationBar(
items: const [
BottomNavigationBarItem(label: "Home", icon: Icon(Icons.home)),
BottomNavigationBarItem(label: "Settings", icon: Icon(Icons.settings))
],
currentIndex: currentIndex,
onTap: (int index) {
//index value here is returned by flutter by the function 'onTap'
setState(() {
currentIndex = index;
//print(currentIndex);
});
},
),
); //Scaffold represents the skeleton of the app(displays white page)
}
}
class SecondPage extends StatelessWidget {
const SecondPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
);
}
}
Images:
Before pressing Click and Settings button
After pressing Click and Settings looks the same
I want the screen to change the ElevatedButton Click to Clicked when onPressed() is triggered and also, the app should be able to switch settings page when the onTap() method is triggered in the bottom navigation bar.
The code worked initially when I refrained from calling an entire page of Scaffold from Material app, but as soon as I changed the part
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: FirstPage()); //<-- this part
}
}
I'm getting this error.
Put your variables outside the build method.Else it will reset to default on every build.
It will be like
class _FirstPageState extends State<FirstPage> {
//here
String buttonName = "Click";
int currentIndex = 0;
#override
Widget build(BuildContext context) {
// Not here
return Scaffold(
appBar: AppBar(
More about StatefulWidget
How to change the text color which is in a different widget on switch with a flutter provider?
When switch is on change text color to red else change to green. Bu don't merge first and second widgets.
When clicked switch button change other widget's text.
`
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(const SwitchApp());
class SwitchApp extends StatelessWidget {
const SwitchApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Switch Sample')),
body: const Center(
child: SwitchExample(),
),
),
);
}
}
class SwitchExample extends StatefulWidget {
const SwitchExample({super.key});
#override
State<SwitchExample> createState() => _SwitchExampleState();
}
class _SwitchExampleState extends State<SwitchExample> {
bool light = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
Switch(
// This bool value toggles the switch.
value: light,
activeColor: Colors.red,
onChanged: (bool value) {
// This is called when the user toggles the switch.
setState(() {
light = value;
});
},
),
MyText()
],
);
}
}
class MyText extends StatelessWidget {
const MyText({super.key});
#override
Widget build(BuildContext context) {
return const Text('Change my color',
style: TextStyle(color: Colors.green));
}
}
`
The easiest way would be to pass the color down into the constructor of MyText widget, since MyText widget is being built as a child of SwitchExample which is handling the switch state.
import 'package:provider/provider.dart';
void main() => runApp(const SwitchApp());
class SwitchApp extends StatelessWidget {
const SwitchApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Switch Sample')),
body: const Center(
child: SwitchExample(),
),
),
);
}
}
class SwitchExample extends StatefulWidget {
const SwitchExample({super.key});
#override
State<SwitchExample> createState() => _SwitchExampleState();
}
class _SwitchExampleState extends State<SwitchExample> {
bool light = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
Switch(
// This bool value toggles the switch.
value: light,
activeColor: Colors.red,
onChanged: (bool value) {
// This is called when the user toggles the switch.
setState(() {
light = value;
});
},
),
MyText(light ? Colors.green : Colors.blue)
],
);
}
}
class MyText extends StatelessWidget {
final Color color;
const MyText(this.color, {super.key});
#override
Widget build(BuildContext context) {
return Text('Change my color',
style: TextStyle(color: color));
}
}
But, if you wanted to use provider so that MyText could be a child widget anywhere below the provider widget in the tree you could use Provider with a ChangeNotifier:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(const SwitchApp());
class ColorModel extends ChangeNotifier {
Color color = Colors.green;
void setColor(Color color) {
this.color = color;
notifyListeners();
}
}
class SwitchApp extends StatelessWidget {
const SwitchApp({super.key});
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<ColorModel>(
create: (context) => ColorModel(),
child: MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Switch Sample')),
body: const Center(
child: SwitchExample(),
),
),
),
);
}
}
class SwitchExample extends StatefulWidget {
const SwitchExample({super.key});
#override
State<SwitchExample> createState() => _SwitchExampleState();
}
class _SwitchExampleState extends State<SwitchExample> {
bool light = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
Switch(
// This bool value toggles the switch.
value: light,
activeColor: Colors.red,
onChanged: (bool value) {
// This is called when the user toggles the switch.
setState(() {
light = value;
});
Provider.of<ColorModel>(context, listen: false)
.setColor(value ? Colors.green : Colors.blue);
},
),
MyText()
],
);
}
}
class MyText extends StatelessWidget {
const MyText({super.key});
#override
Widget build(BuildContext context) {
return Consumer<ColorModel>(builder: (context, state, _) {
return Text('Change my color', style: TextStyle(color: state.color));
});
}
}
Check out flutter's docs for more info: https://docs.flutter.dev/development/data-and-backend/state-mgmt/simple
Let said I have a widget "mySonWidget" inside this widget I have a function "updateIconColor", I want to call that function from the father of "mySonWidget"
I did something similiar with callback but this is not the same scenario.
I saw people other widgets doing similar thing with controllers, but I don't know how create a custom controller.
How can I do it?
HELP
class mySonWidget extends StatefulWidget {
#override
_mySonWidgetState createState() => _mySonWidgetState();
}
class _mySonWidgetState extends State<mySonWidget> {
var _iconColor = Colors.red[500];
void updateIconColor() {
setState(() {
print('updateIconColor was called');
_iconColor = Colors.green;
});
}
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(0),
child: IconButton(
icon: (Icon(Icons.star)),
color: _iconColor,
onPressed: () {},
),
);
}
}
father example:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Column(children: <Widget>[
mySonWidget(),
FlatButton(
onPressed: () {
/*..Call Function inside mySonWidget (updateIconColor) ..*/
},
child: Text(
"Change Color",
),
),
]),
),
);
}
}
You can copy paste run full code below
You can use GlobalKey can use _key.currentState to call updateIconColor()
code snippet
GlobalKey _key = GlobalKey();
...
mySonWidget(key: _key),
FlatButton(
onPressed: () {
final _mySonWidgetState _state = _key.currentState;
_state.updateIconColor();
},
...
class mySonWidget extends StatefulWidget {
mySonWidget({Key key}) : super(key: key);
working demo
full code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
GlobalKey _key = GlobalKey();
#override
Widget build(BuildContext context) {
return MaterialApp(
//theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: SafeArea(
child: Scaffold(
body: Column(children: <Widget>[
mySonWidget(key: _key),
FlatButton(
onPressed: () {
final _mySonWidgetState _state = _key.currentState;
_state.updateIconColor();
},
child: Text(
"Change Color",
),
),
]),
),
),
);
}
}
class mySonWidget extends StatefulWidget {
mySonWidget({Key key}) : super(key: key);
#override
_mySonWidgetState createState() => _mySonWidgetState();
}
class _mySonWidgetState extends State<mySonWidget> {
var _iconColor = Colors.red[500];
void updateIconColor() {
setState(() {
print('updateIconColor was called');
_iconColor = Colors.green;
});
}
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(0),
child: IconButton(
icon: (Icon(Icons.star)),
color: _iconColor,
onPressed: () {},
),
);
}
}
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],
),
);
}
}
i am studying key in flutter. and in explanation, when i want swap widget in statefulWidget i need to add key value. because when flutter check element structure if type, state are not same they don't response. this is how i understand.
void main() => runApp(new MaterialApp(home: PositionedTiles()));
class PositionedTiles extends StatefulWidget {
#override
State<StatefulWidget> createState() => PositionedTilesState();
}
class PositionedTilesState extends State<PositionedTiles> {
List<Widget> tiles = [
StatefulColorfulTile(key: UniqueKey()), // Keys added here
StatefulColorfulTile(key: UniqueKey()),
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(children: tiles),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.sentiment_very_satisfied), onPressed: swapTiles),
);
}
swapTiles() {
setState(() {
tiles.insert(1, tiles.removeAt(0));
});
}
}
class StatefulColorfulTile extends StatefulWidget {
StatefulColorfulTile({Key key}) : super(key: key); // NEW CONSTRUCTOR
#override
ColorfulTileState createState() => ColorfulTileState();
}
class ColorfulTileState extends State<ColorfulTile> {
Color myColor;
#override
void initState() {
super.initState();
myColor = UniqueColorGenerator.getColor();
}
#override
Widget build(BuildContext context) {
return Container(
color: myColor,
child: Padding(
padding: EdgeInsets.all(70.0),
));
}
}
but i saw this code.
Widget build(BuildContext context) {
return Column(
children: [
value
? const SizedBox()
: const Placeholder(),
GestureDetector(
onTap: () {
setState(() {
value = !value;
});
},
child: Container(
width: 100,
height: 100,
color: Colors.red,
),
),
!value
? const SizedBox()
: const Placeholder(),
],
);
}
this code is also use statefulWidget. in this code when user taps Box it's changed but i think there're no key value and in element structure there are different type(one is SizedBox and the other is placeHolder) so i think there aren't changed. why they're changed? what i misunderstand?