Flutter color animation produces unwanted flashing - flutter

I want to animate from this white color with slight opacity to a black color with very high opacity.
Colors are
Color(0xB1FFFFFF) - White
Color(0x0B000000) - Black
This will produce an animation with a "flash". However, this should not happen.
What am I doing wrong? When doing the same animation with css, it does not "flash".
This is how it looks when using css and how I would expect it:
The jump to white is not important, it's just the animation restarting.
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(const AnimatedContainerApp());
class AnimatedContainerApp extends StatefulWidget {
const AnimatedContainerApp({Key? key}) : super(key: key);
#override
_AnimatedContainerAppState createState() => _AnimatedContainerAppState();
}
class _AnimatedContainerAppState extends State<AnimatedContainerApp> {
double _width = 50;
double _height = 50;
Color _color = Color(0xB1FFFFFF);
BorderRadiusGeometry _borderRadius = BorderRadius.circular(8);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('AnimatedContainer Demo'),
),
body: Center(
child: AnimatedContainer(
width: _width,
height: _height,
decoration: BoxDecoration(
color: _color,
borderRadius: _borderRadius,
),
duration: const Duration(seconds: 1),
curve: Curves.fastOutSlowIn,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
_color = Color(0x0B000000);
});
},
child: const Icon(Icons.play_arrow),
),
),
);
}
}

I found an alternative of this using Stack widget.
class _AnimatedContainerAppState extends State<AnimatedContainerApp>{
double _width = 50;
double _height = 50;
Color colorA = Color(0xB1FFFFFF);
Color colorB = Color(0x9F000000); //change your color here
double opacity = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.pink,
appBar: AppBar(
title: const Text('AnimatedContainer Demo'),
),
body: Center(
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: SizedBox(
key: const ValueKey("C"),
width: _width,
height: _height,
child: Stack(
fit: StackFit.expand,
children: [
//fixed color
AnimatedOpacity(
opacity: (1 - opacity).abs(),
duration: const Duration(seconds: 1),
child: ColoredBox(color: colorA),
),
AnimatedOpacity(
opacity: opacity,
duration: const Duration(seconds: 1),
child: ColoredBox(color: colorB),
),
],
),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
opacity = opacity == 0 ? 1 : 0;
});
},
child: const Icon(Icons.play_arrow),
),
),
);
}
}
You can also try with others animated widget like AnimatedCrossFade , FadeTransition etc.

I now understand why this "issue" exists.
Since both colors vary a lot both in opacity and color, a basic interpolation by time over the rgba values produces this behavior.
Whilst the alpha is being interpolated, the color is so too, and this creates a temporary darker color in between.
Dart interpolates colors by mapping the difference in colors to the time in the rgba spectrum.
a + (b - a) * t
This article describes very good what happens and other possible solutions for interpolating colors.
Also using HSL instead of rgba might also solve the issue. However, converting the color to HSL every time is not near as performant as flat rgba interpolation. Dart is all about performance, so this might be why they went for this very basic approach.
My personal solution to the problem was a very basic one though. I asked the designer to bring the colors closer together :)

Related

How to randomly change rgb color?

In the application, when you click on the screen, you need to change the color to random, but it must be in RGB format. How can I get a random RGB color?
class _HomePageState extends State<HomePage> {
Color _color = Colors.white;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Color picker"),
centerTitle: true,
),
body: GestureDetector(
onTap: () {
setState(() {
_color = ...
});
},
child: Container(
color: _color,
padding: const EdgeInsets.all(20),
),
),
);
}
}
You can do like this
class _HomePageState extends State<HomePage> {
Random random = Random();
Color _color = Colors.white;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Color picker"),
centerTitle: true,
),
body: GestureDetector(
onTap: () {
setState(() {
_color = Color.fromRGBO(
random.nextInt(255),
random.nextInt(255),
random.nextInt(255),
1,
);
});
},
child: Container(
color: _color,
padding: const EdgeInsets.all(20),
),
),
);
}
}
Best wat to get a random color:
Color((math.Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0)
This import is required.
import 'dart:math' as math;
or
Colors.primaries[Random().nextInt(Colors.primaries.length)]
The other solutions (so far) are literally creating an arbitrary RGB value as requested, but I suspect the OP wants a "different but workable color". Random RGB is a poor way to generate those. You want randomness in the HSV direction.
In that case, supply random values to HSVColor.fromAHSV. Alpha should be 1.0 (opaque). Hue can be anything from 0.0 to 360.0. Saturation should probably be between 0.5 and 1.0 (more color than grey). Value should be between 0.0 and 0.5 if you have contrasting light text, and 0.5 to 1.0 if you have contrasting dark text.

Flutter - Container with ring progress indicator border

I am trying to achieve a container that will have a progress indicator border, like in this image:
I've tried to achieve it using a container inside another container (1 container for the outside white border, and one container for the inside blue background with the icon), but I can't achieve the progress indicator effect.
Does anyone know how can I achieve this?
Thank you
If you don't want to use a CustomPainter you can try to achieve that with a Stack widget
You can see this example in DartPad
Use the value property on the second CircularProgressIndicator to update the value with setState or any other State Management technique you like
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Stack(
children: const [
CircleAvatar(
backgroundColor: Colors.white,
radius: 24,
child: Icon(Icons.check),
),
SizedBox(
width: 50,
height: 50,
child: CircularProgressIndicator(
color: Colors.grey,
value: 1,
),
),
SizedBox(
width: 50,
height: 50,
child: CircularProgressIndicator(
color: Colors.blue,
value: .3, // Change this value to update the progress
),
),
],
);
}
}
There is a widget called CircularProgressIndicator that seems to be exactly what you're after.
How to use it:
CircularProgressIndicator(
backgroundColor: Colors.white,
color: Colors.purple.withAlpha(100),
strokeWidth: 5,
value: value, //
),
backgroundColor: for the white background
color: for the purple overlay
strokeWidth: for the thickness that you want
value: the actual progress of the indicator
And to have the arrow on top just use a round white Container (use a BoxDecoration with shape: BoxShape.circle to make it a circle), and put the arrow on top of it using the Stack widget.
Hope this helps!
class ProgressPainter extends CustomPainter {
final double value;
double deg2rad(double deg) => deg * pi / 180;
ProgressPainter({
required this.value,
});
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()..color = Colors.blueGrey;
final rect = Rect.fromCenter(
center: Offset(size.height / 2, size.width / 2),
width: size.width,
height: size.height);
canvas.drawArc(
rect,
deg2rad(-90),
deg2rad(
(value * 360) / 100, // % to degree
),
true,
paint);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
And use
CustomPaint(
painter: ProgressPainter(value: sliderVal),
child: const SizedBox(
height: 100,
width: 100,
child: Icon( // your inner widget
Icons.ac_unit,
size: 100,
),
),
),
Tested widget:
class _MyHomePageState extends State<MyHomePage> {
double sliderVal = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.title)),
body: Center(
child: Column(
children: [
CustomPaint(
painter: ProgressPainter(value: sliderVal),
child: const SizedBox(
height: 100,
width: 100,
child: Icon(
Icons.ac_unit,
size: 100,
),
),
),
Slider(
value: sliderVal,
min: 0,
max: 100,
onChanged: (value) {
setState(() {
sliderVal = value;
});
},
)
],
),
),
);
}
}

Flutter - How to add a widget inside the track of a Slider widget

I have a simple Slider widget and I want to add multiple containers inside the track at different points.
Needed Outcome Image
Current Code
Slider(
max: 500,
value: _playerValue,
onChanged: (double value){
setState(() {
_playerValue = value;
});
},
),
I do not think this is possible only by using the SliderThemeData; or at least, no way that I am aware of. However, you can achieve this behaviour using Stack, with the markers shown behind a translucent track of the slider. Demo snippet below.
One thing to note is to make sure the container that has the stack of markers has horizontal margin that is same as the slider's overlay radius; otherwise, the marker container becomes wider than the track.
IMPORTANT: I put this snippet together rapidly only to demonstrate the possibility of showing markers behind a slider's track. The dimensions of the track and the markers are hard-coded, which is very likely not what you want. I do not recommend using it as is, but only as a guide.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Slider with Markers',
theme: ThemeData(
primarySwatch: Colors.blue,
brightness: Brightness.dark,
),
home: Home(),
);
}
}
class Home extends StatefulWidget {
Home({Key? key}) : super(key: key);
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
double sliderValue = 50;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text("Slider With Markers"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTrackColor: Colors.white60,
inactiveTrackColor: Colors.white30,
thumbColor: Colors.white,
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 10),
overlayShape: RoundSliderOverlayShape(overlayRadius: 12),
trackHeight: 12,
),
child: Stack(
alignment: Alignment.center,
children: [
Container(
width: double.infinity,
margin: const EdgeInsets.symmetric(horizontal: 12),
child: Stack(
alignment: Alignment.centerLeft,
children: <Widget>[
marker(left: 15, width: 30),
marker(left: 150, width: 50),
marker(left: 300, width: 100),
],
),
),
Slider(
value: sliderValue,
min: 0,
max: 100,
onChanged: ((value) {
setState(() {
sliderValue = value;
});
}),
),
],
),
)
],
),
),
);
}
Widget marker({required double left, required double width}) {
return Container(
height: 12,
width: width,
margin: EdgeInsets.only(left: left),
decoration: BoxDecoration(
color: Colors.amber,
borderRadius: BorderRadius.circular(18),
),
);
}
}

How to draw container outside screen width in flutter?

How can I create a container with rounded corners as shown below?
I tried using container with width more than the screen width. But that constraints it inside the screen. I tried using an OverFlow box, but couldn't get the same result as well. I don't want to use clipRect to make this as I want to apply animation on the corners.
Edit: Added container snippet with the resulting outcome to clear doubts
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: Align(
alignment: Alignment.bottomCenter,
child: Container(
height: 500,
decoration: BoxDecoration(
color: Colors.green, borderRadius: BorderRadius.circular(500)),
),
),
);
}
I have managed to get similar to what I want by using scale transformation. Would like to see a different approach though.
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: Align(
alignment: Alignment.bottomCenter,
child: Transform.scale(
scale: 1.7,
child: Container(
height: 400,
decoration: BoxDecoration(
color: Colors.green, borderRadius: BorderRadius.circular(200)),
),
),
),
);
}
I have done this using clippath. If you change size of Container then Clippath size automatically change as per Container size.
You can modify Path for different shape as per your requirement so this is very useful.
Here, I just use ClipPath widget and I create MyCustomShape class for modify shape of child Container widget
class Example extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black87,
body: ClipPath(
clipper: MyCustomShape(),
child: Container(
color: Colors.green[800],
height: double.infinity,
),
),
);
}
}
class MyCustomShape extends CustomClipper<Path> {
#override
Path getClip(Size size) {
Path path = new Path(); // use for shape your container
path.lineTo(0.0, 100);
path.quadraticBezierTo(size.width * 0.5, 0.0, size.width, 100);
path.lineTo(size.width, size.height);
path.lineTo(0.0, size.height);
path.close();
return path;
}
after reading your questions and comments many time i hope this what you are looking for
class dummy2 extends StatefulWidget {
#override
_dummy2State createState() => _dummy2State();
}
class _dummy2State extends State<dummy2> with TickerProviderStateMixin {
AnimationController controller;
Animation<double> animaton;
#override
initState() {
super.initState();
controller =
AnimationController(vsync: this, duration: Duration(seconds: 1));
animaton = Tween<double>(begin: 1, end: 2.5).animate(
CurvedAnimation(parent: controller, curve: Curves.fastOutSlowIn));
animaton.addListener(() {
setState(() {});
});
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
actions: <Widget>[
FlatButton(
onPressed: () {
controller.forward();
},
child: Text('forward')),
FlatButton(
onPressed: () {
controller.reverse();
},
child: Text('reverse'))
],
),
body: Transform.scale(
child: Container(
decoration: BoxDecoration(
color: Colors.deepPurple, shape: BoxShape.circle),
),
scale: animaton.value,
)));
}
}
I believe you should use SafeArea within a container to fill it. Like this:
Container(
color: Colors.blue,
child: SafeArea(child: ### // you nav or functions),
),
See: https://api.flutter.dev/flutter/widgets/SafeArea-class.html

Flutter: How manage transition with MediaQuery's values on animatedTransition Widget

first at all, i apologize for my english.
And second i have a trouble manage values for animateTransition. As you can see i have a card widget with a position at the right of the screen and MaterialButton in the center of the screen.
The positioned widget's values are give by MediaQuery Class and after save in the position variable, but when i try change the position's value and call setSate function, the build method redo and initialize the variables and again the new value is the same always because of that (i think that), and for that never will see a transition.
i show the code.
class _DashboardState extends State<Dashboard> {
final double blur = 30;
final double offset = 20;
final double top = 25;
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
double position = -(size.width) * 0.8;
return WillPopScope(
onWillPop: () async =>
SystemChannels.platform.invokeMethod('SystemNavigator.pop'),
child: MaterialApp(
home: Scaffold(
body: SafeArea(
child: Stack(
alignment: Alignment.center,
children: <Widget>[
Positioned(
right: position,
child: AnimatedContainer(
duration: Duration(milliseconds: 500),
curve: Curves.decelerate,
child: Card(
elevation: 8,
color: Colors.red,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: Container(
width: size.width * 0.9,
height: size.height * 0.8,
),
),
),
),
Center(
child: MaterialButton(
color: Colors.black,
child: Text("mover"),
onPressed: () {
setState(() {
position = 0;
});
},
),
)
],
),
),
),
),
);
}
}
when i try to take out the variables i can't because de context dependency in mediaQuery and i can't initialize the request variables to get size of device, and put in the initial state. any advice for manage this type of problem.
Thanks for your attention