Easiest way to make a skeleton screen when data is loading - flutter

I am new to flutter and i am trying to make something that looks like in the example included in the snippet.
What is the easiest way to do this with dart and flutter.
basically i want to apply a shine animation to a container when the data from the server is still being downloaded.
Thanks
div {
margin: auto;
width: 500px;
height: 600px; /* change height to see repeat-y behavior */
background-image:
radial-gradient( circle 50px at 50px 50px, lightgray 99%, transparent 0 ),
linear-gradient( 100deg, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.5) 50%, rgba(255, 255, 255, 0) 80% ),
linear-gradient( lightgray 20px, transparent 0 ),
linear-gradient( lightgray 20px, transparent 0 ),
linear-gradient( lightgray 20px, transparent 0 ),
linear-gradient( lightgray 20px, transparent 0 );
background-repeat: repeat-y;
background-size:
100px 200px, /* circle */
50px 200px, /* highlight */
150px 200px,
350px 200px,
300px 200px,
250px 200px;
background-position:
0 0, /* circle */
0 0, /* highlight */
120px 0,
120px 40px,
120px 80px,
120px 120px;
animation: shine 1s infinite;
}
#keyframes shine {
to {
background-position:
0 0,
100% 0, /* move highlight to right */
120px 0,
120px 40px,
120px 80px,
120px 120px;
}
}
<div></div>

You can combine a Stack and Positioned.fill with a FractionallySizedBox to position such gradient on the top of another widget.
Then you can combine it to an AnimationController, an AnimatedBuilder and an Align or FractionallySizedBox to animate the position of the gradient over time.
You'd have this :
class LoadAnimation extends StatefulWidget {
final Widget child;
LoadAnimation({#required this.child, Key key}) : super(key: key);
#override
_LoadAnimationState createState() => _LoadAnimationState();
}
class _LoadAnimationState extends State<LoadAnimation>
with SingleTickerProviderStateMixin {
AnimationController controller;
#override
void initState() {
controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
)..repeat();
super.initState();
}
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
widget.child,
Positioned.fill(
child: ClipRect(
child: AnimatedBuilder(
animation: controller,
builder: (context, child) {
return FractionallySizedBox(
widthFactor: .2,
alignment: AlignmentGeometryTween(
begin: Alignment(-1.0 - .2 * 3, .0),
end: Alignment(1.0 + .2 * 3, .0),
).chain(CurveTween(curve: Curves.easeOut)).evaluate(controller),
child: child,
);
},
child: const DecoratedBox(
decoration: const BoxDecoration(
gradient: const LinearGradient(
colors: const [
Color.fromARGB(0, 255, 255, 255),
Colors.white,
],
),
),
),
)),
),
],
);
}
}
Which you can then use by wrapping any given widget :
LoadAnimation(
child: Container(
height: 100.0,
width: 200.0,
color: Colors.lime,
),
),

Although Rèmi posted a very nice solution, this package should also be mentioned: https://pub.dev/packages/shimmer

Related

Flutter Container Column is not aligning correclty

I simply would like to have a couple of Containers inside a Column but somehow I can not align them correctly... This is my setup:
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ShimmerContainer(
height: 50,
width: 200,
),
ShimmerContainer(
height: 50,
width: 200,
),
ShimmerContainer(
height: 50,
width: 200,
)
],
),
And somehow my Containers are all aligned in the center. The really weird thing is that if I wrap one of these ShimmerContainers inside a Align with Alignment.centerLeft all other containers are also aligning left. I have no idea why this happens.
This is my ShimmerContainer:
class ShimmerContainer extends StatelessWidget {
final double? height;
final double? width;
final double borderRadius;
const ShimmerContainer({
this.height,
this.width,
this.borderRadius = 0,
});
#override
Widget build(BuildContext context) {
return Shimmer.fromColors(
baseColor: Colors.grey[200]!,
highlightColor: Colors.white,
child: Container(
height: height != null ? height!.scaled : null,
width: width != null ? width!.scaled : null,
decoration: BoxDecoration(
color: white,
borderRadius: BorderRadius.circular(
borderRadius,
),
),
),
);
}
}
Ouput of your code
I added background color to view properly.
I think you should use flutter clean and run again. Else error is happening from parent side.Does it solve your issue?

Flutter: center overflowed image in Stack

I have an image and I want to place it in the Stack widget which has smaller size than initial image.
Also I need to center this image inside smaller widget.
For now I found one approach which allows just to set overflowing image size inside smaller parent widget.
Container(
width: 50.0,
height: 50.0,
child: Stack(
children: [
Positioned(
width: 150.0,
height: 150.0,
child: Image.asset('assets/images/example.jpg'),
)
]
)
)
But in this case I have to center image by setting top and left offsets manually because any alignment doesn't work in this case.
Container(
width: 50.0,
height: 50.0,
child: Stack(
children: [
Positioned(
width: 150.0,
height: 150.0,
child: Align(
alignment: Alignment.center,
child: Image.asset('assets/images/example.jpg'),
)
)
]
)
)
If I use Positioned.fill - I can't then set the size of overflowing widget space, so that it doesn't work too.
If I increase the scale of an image it just makes initial image smaller rather than increase size of an image and crop overflowed space.
I need to make image overflowed and centered automatically by some alignment property.
Can't find solution for this issue yet.
I don't know how to do it in Flutter but with CSS it is very easy:
div {
width: 100px;
height: 100px;
background: url('https://img.icons8.com/clouds/2x/sun.png') no-repeat;
background-size: 200px 200px;
background-position: center;
border: 1px solid black;
border-radius: 5px;
}
<div>
</div>
If I understood you correctly the same effect as per your CSS could be achieved by Centering the Container and setting the Image BoxFit to none. Please see the code below :
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: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Image Demo'),
),
body: Center(
child: Container(
width: 100.0,
height: 100.0,
child: Image.network('https://img.icons8.com/clouds/2x/sun.png',
fit: BoxFit.none),
),
),
);
}
}

How to design this custom drawer in flutter

Hey I tried in several ways now to create following Drawer design:
When I used a OverflowBox compared with a ClipOval somehow this design was the result:
ClipOval(
child: OverflowBox(
maxHeight: double.infinity,
maxWidth: double.infinity,
child: DecoratedBox(
decoration: BoxDecoration(
color: AppColors.backgroundColor
),
child: Container(
height: AppConstants.screenHeight,
width: AppConstants.screenWidth,
),
),
),
);
How can i get the above design. I know it must be done for sure with a overflowbox and some kind of clipper but somehow I cant make it work.
I would do that with a transparent drawer on which you paint over your shape with a CustomPainter. Make the drawer transparent by using a Theme and the canvasColor: Colors.transparent Important: Set the elevation of the drawer to 0, otherwise you see the edge of the transparent drawer.
Scaffold(
appBar: AppBar(title: Text(title)),
body: Center(child: Text('Custom Drawer Shape')),
drawer: Theme(
data: Theme.of(context).copyWith(
canvasColor: Colors.transparent, // set the Color of the drawer transparent; we'll paint above it with the shape
),
child: Drawer(
elevation: 0, // set elevation to 0, otherwise you see the transparent drawer a little bit
child: Container(
padding: EdgeInsets.fromLTRB(0, 0, 0, 0),
child: CustomPaint(
painter: DrawerPainter(color: Colors.white), // this is your custom painter
child: ListView(
padding: EdgeInsets.fromLTRB(0, 20, 0, 0),
children: <Widget>[
// Add your menu e.g. with ListTile
],
),
),
),
),
),
);
// Class which draws the custom shape
class DrawerPainter extends CustomPainter {
final Color color;
DrawerPainter({this.color = Colors.black});
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = color
..strokeWidth = 3
..style = PaintingStyle.fill;
canvas.drawPath(getShapePath(size.width, size.height), paint);
}
// This is the path of the shape we want to draw
Path getShapePath(double x, double y) {
return Path()
..moveTo(0, 0)
..lineTo(x / 2, 0)
..quadraticBezierTo(x, y / 2, x / 2, y)
..lineTo(0, y);
}
#override
bool shouldRepaint(DrawerPainter oldDelegate) {
return oldDelegate.color != color;
}
}

If I created a widget, how can I set the width or height as screen ratio?

If I created a widget, how can I set the width or height as screen ratio?
class HomeContent extends StatelessWidget{
#override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Container(
child: Image.network("http://a1.att.hudong.com/60/38/01200000194369136323385641912.jpg"),
width: 500,
height: 500,
decoration: BoxDecoration(
color: Colors.yellow,
),
),
);
}
}
You see my upper code, I want the width:Center_height as 1:2, how can I implement?
I mean the Center be carpeted with the screen page, the Container's height : Center's height = 1:2.
instead of directly passing value,
try using MediaQuery class.
Example
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
MediaQuery class will return the height / width of the current device, and you can directly use it accordingly, you can also use it like this,
width: MediaQuery.of(context).size.width / 2,
here, widget will occupy the half width available in current screen.
Use Mediaquery forheight and Width
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
And implement like this
class HomeContent extends StatelessWidget{
#override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Container(
child: Image.network("http://a1.att.hudong.com/60/38/01200000194369136323385641912.jpg"),
width: width,
height: height,
decoration: BoxDecoration(
color: Colors.yellow,
),
),
);
}
}

Set two different colours to single container

I am trying to achieve a custom design dynamically from my button. I have designed this button from a Container with InkWell. But I am not getting a correct way how I can set 2 different colours to this button based on received values from my API.
For reference here is the button:
In this button grey part is container background color. Then I added a image in this container. Now the red color should dynamic with height received from server.
But I am not getting a correct way how I can do it.
You can easily achieve this with container and linear gradient. Pass in the colors as gradient and define appropriate stops at the required percentages.
Example:
#override
Widget build(BuildContext context) {
final Color background = Colors.grey;
final Color fill = Colors.redAccent;
final List<Color> gradient = [
background,
background,
fill,
fill,
];
final double fillPercent = 56.23; // fills 56.23% for container from bottom
final double fillStop = (100 - fillPercent) / 100;
final List<double> stops = [0.0, fillStop, fillStop, 1.0];
return Container(
child: Icon(Icons.forward),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: gradient,
stops: stops,
end: Alignment.bottomCenter,
begin: Alignment.topCenter,
),
),
);
}
Hope this helps!
You can do it using gradient but in case you want to create your own Container to get more customization, here you have:
class MyCustomContainer extends StatelessWidget {
final Color backgroundColor;
final Color progressColor;
final double progress;
final double size;
const MyCustomContainer({
Key key,
this.backgroundColor = Colors.grey,
this.progressColor = Colors.red,
#required this.progress,
#required this.size,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.circular(size / 2),
child: SizedBox(
height: size,
width: size,
child: Stack(
children: [
Container(
color: backgroundColor,
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
height: size * progress,
color: progressColor,
),
),
],
),
),
);
}
}
Usage
Center(
child: MyCustomContainer(
progress: 0.7,
size: 100,
backgroundColor: Colors.grey,
progressColor: Colors.red,
),
),
Result
And of course you can customize that widget to receive a child and put it at the center.