Flutter Why Shimmer loading effect doesn't work on IOS? - flutter

I follow the flutter documentation create a shimmer loading effect class like below
import 'package:flutter/widgets.dart';
const _shimmerGradient = LinearGradient(
colors: [
Color(0xFFEBEBF4),
Color(0xFFF00000),
Color(0xFFEBEBF4),
],
stops: [
0.1,
0.3,
0.4,
],
begin: Alignment(-1.0, -0.3),
end: Alignment(1.0, 0.3),
tileMode: TileMode.clamp,
);
class ShimmerLoading extends StatefulWidget {
const ShimmerLoading({
super.key,
required this.isLoading,
required this.child,
});
final bool isLoading;
final Widget child;
#override
State<ShimmerLoading> createState() => _ShimmerLoadingState();
}
class _ShimmerLoadingState extends State<ShimmerLoading> {
#override
Widget build(BuildContext context) {
if (!widget.isLoading) {
return widget.child;
}
return ShaderMask(
blendMode: BlendMode.srcATop,
shaderCallback: (bounds) {
return _shimmerGradient.createShader(bounds);
},
child: widget.child,
);
}
}
and i implement it on test screen.
test_screen.dart
class _TestScreenState extends BaseConsumerState<TestScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
...
ShimmerLoading(
isLoading: true,
child: Container(
width: 200,
height: 50,
color: Colors.red,
),
),
...
}
}
I tested it on the emulator.
It works on Android but doesn't work on IOS.
How can I make sure that will work on the real device?
IOS' screen
the black square is a shimmer loading effect.

Try Shimmer Package
Package From here

Related

Flutter: How do I add a floatingactionbutton inside a container

I have the below code and I would like to add a floatingactionbutton to turn on and off the camera flash. I tried different ways but was not able to do it. Any suggestions would help. I tired different packages like torch, torch-light etc and it didn't work I think is because I have already called the camera package
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
class CameraScreen extends StatefulWidget {
CameraScreen({Key key, #required this.controller}) : super(key: key);
final CameraController controller;
#override
_CameraScreenState createState() => _CameraScreenState();
}
class _CameraScreenState extends State<CameraScreen> {
#override
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size.width;
return Container(
child: ShaderMask(
shaderCallback: (rect) {
return LinearGradient(
begin: Alignment.topCenter,
end: Alignment.center,
colors: [Colors.black, Colors.transparent])
.createShader(Rect.fromLTRB(0, 0, rect.width, rect.height / 4));
},
blendMode: BlendMode.darken,
child: Transform.scale(
scale: 1.0,
child: AspectRatio(
aspectRatio: MediaQuery.of(context).size.aspectRatio,
child: OverflowBox(
alignment: Alignment.center,
child: FittedBox(
fit: BoxFit.fitHeight,
child: Container(
width: size,
height: size / widget.controller.value.aspectRatio,
child: Stack(
children: <Widget>[
CameraPreview(widget.controller),
],
),
),
),
),
),
),
)
);
}
#override
void dispose() {
super.dispose();
}
}
You can wrap your widget with scaffold and can add float button inside the scaffold like below :
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size.width;
return Scaffold(
floatingActionButton: FloatingActionButton(onPressed: (){},),
body: ShaderMask(
shaderCallback: (rect) {
return const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.center,
colors: [Colors.black, Colors.transparent])
.createShader(Rect.fr
///// your code ///////

How to change scale and re-scale of container continiously by using animations?

I am begginner flutter developer, I want to take a support, I designed welcome page and I put background image to the container, but I want it to scale and re-scale forever while user on welcome page, I just want to learn teory of animation controller and how to use them. How can I implement this to my code. Thank you... Have a nice day.
import 'package:flutter/material.dart';
import 'package:animated_text_kit/animated_text_kit.dart';
import 'file:///C:/Users/yagiz/AndroidStudioProjects/naber/lib/screens/login_screen.dart';
import 'file:///C:/Users/yagiz/AndroidStudioProjects/naber/lib/screens/registration_screen.dart';
import '../widgets.dart';
import 'package:naber/constants.dart';
class WelcomeScreen extends StatefulWidget {
static String id="welcome_screen";
#override
_WelcomeScreenState createState() => _WelcomeScreenState();
}
class _WelcomeScreenState extends State<WelcomeScreen> with TickerProviderStateMixin {
bool isFinished;
AnimationController controllerForContainerHeight;
AnimationController controllerForContainerWidth;
Animation scaleAnimationForContainerWidth;
Animation scaleAnimationForContainerHeight;
#override
void initState(){
super.initState();
controllerForContainerWidth=AnimationController(duration:(Duration(seconds: 2)),vsync: this);
scaleAnimationForContainerWidth=Tween<double>(begin: 1080, end: 1380).animate(controllerForContainerWidth);
controllerForContainerWidth.forward();
controllerForContainerWidth.addListener(() {
setState(() {
});
});
controllerForContainerWidth.addStatusListener((status) {
if(status==AnimationStatus.completed){
controllerForContainerWidth.reverse();
}
if(status==AnimationStatus.dismissed){
controllerForContainerWidth.forward();
}
});
controllerForContainerHeight=AnimationController(duration:(Duration(seconds: 2)),vsync: this);
scaleAnimationForContainerHeight=Tween<double>(begin: 1920, end: 2220).animate(controllerForContainerHeight);
controllerForContainerHeight.forward();
controllerForContainerHeight.addListener(() {
setState(() {
});
});
controllerForContainerHeight.addStatusListener((status) {
setState(() {
if(status==AnimationStatus.completed){
controllerForContainerHeight.reverse();
}
if(status==AnimationStatus.dismissed){
controllerForContainerHeight.forward();
}
});
});
controllerForContainerHeight.repeat(reverse: true);
}
#override
void dispose(){
controllerForContainerWidth.dispose();
controllerForContainerHeight.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: AnimatedBuilder(
animation: controllerForContainerHeight,
builder: (BuildContext context,_){
return AnimatedBuilder(
animation: controllerForContainerWidth,
builder: (BuildContext context,_){
return Container(
width: controllerForContainerWidth.value,
height: controllerForContainerHeight.value,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(kWelcomeScreenBackgroundImage),
fit: BoxFit.cover,
),
),
child: Padding(
padding: EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
textBaseline: TextBaseline.alphabetic,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Hero(
tag: "logo",
child: Container(
child: Image.asset(kLogoImage),
height: 140,
),
),
TypewriterAnimatedTextKit(
speed: Duration(milliseconds:200),
text:[kWelcomePageText],
textStyle: kWelcomePageTextStyle,
),
],
),
SizedBox(
height: 70,
),
WelcomeScreenButtons(text:kLoginText,color1:kLoginButtonColor1,
color2:kLoginButtonColor2,
color3:kLoginButtonColor3,route: LoginScreen.id),
SizedBox(height: 15),
WelcomeScreenButtons(text:kRegistrationText,color1:kRegisterButtonColor1,
color2:kRegisterButtonColor2,
color3:kRegisterButtonColor3,route: RegistrationScreen.id),
],
),
),
);
}
);
}
),
);
}
}

Flutter Stack Widget: Bound a layer's size to the size of the underlying layer

So I have a Card with a column (dynamic size). I want to display an overlay (semi transparent + icon) over the whole card, without altering the card's size.
I have coded an example by using a Stack widget. Run on DartPad
The problem is, that the overlay should adapt to the size of the content in the layer below.
But how does the Overlay know the size of that other layer?
double.infinity won't work, because the size of the card is not fixed and can't be fixed, because the content has a dynamic size.
Card(
child: Stack(
children: [
// Content
Padding(
padding: EdgeInsets.all(10),
child: Column(mainAxisSize: MainAxisSize.min, children: [
// Content of dynamic size
Text("q23123"),
Text("q23123"),
Text("q23123"),
RaisedButton(
child: Text("Display Overlay"),
onPressed: (() {
if (_showOverlay == false) {
setState(() {
_showOverlay = true;
});
}
}),
),
]),
),
// Overlay
_showOverlay == true
? Container(
height: double.infinity, // Overlay is too big -> should adabt to
width: double.infinity, // the size of the layer underneath
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [Colors.transparent, Colors.red],
),
),
)
: Container( // Overlay deactivated
height: 0,
width: 0,
),
],
),
color: Colors.blue,
))
You can copy paste run full code below
In this case, you can use GlobalKey to get yourRenderBox.size
code snippet
class _MyHomePageState extends State<MyHomePage> {
bool _showOverlay = false;
GlobalKey _key = GlobalKey();
Size _getSizes() {
final RenderBox yourRenderBox = _key.currentContext.findRenderObject();
return yourRenderBox.size;
}
#override
Widget build(BuildContext context) {
...
Padding(
key: _key,
...
// Overlay
_showOverlay == true
? Container(
height: _getSizes().height,
width: _getSizes().width,
working demo
full code
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(title: 'Flutter Problem'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _showOverlay = false;
GlobalKey _key = GlobalKey();
Size _getSizes() {
final RenderBox yourRenderBox = _key.currentContext.findRenderObject();
return yourRenderBox.size;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Card(
child: Stack(
children: [
// Content
Padding(
key: _key,
padding: EdgeInsets.all(10),
child: Column(mainAxisSize: MainAxisSize.min, children: [
// Content of dynamic size
Text("q23123"),
Text("q23123"),
Text("q23123"),
RaisedButton(
child: Text("Display Overlay"),
onPressed: (() {
if (_showOverlay == false) {
setState(() {
_showOverlay = true;
});
}
}),
),
]),
),
// Overlay
_showOverlay == true
? Container(
height: _getSizes().height,
width: _getSizes().width,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [Colors.transparent, Colors.red],
),
),
)
: Container(
// Overlay deactivated
height: 0,
width: 0,
),
],
),
color: Colors.blue,
)));
}
}
Make Container as a parent of your widget, modified code :
Container(
decoration: _showOverlay ? BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [Colors.transparent, Colors.red],
),
)
: null,
child: Padding(
padding: EdgeInsets.all(10),
child: Column(mainAxisSize: MainAxisSize.min,
children: [
// Content of dynamic size
Text("q23123"),
Text("q23123"),
Text("q23123"),
RaisedButton(
child: Text("Display Overlay"),
onPressed: (() {
if (_showOverlay == false) {
setState(() {
_showOverlay = true;
});
}
}),
),
]),
),
),
I was having this same issue, and as of september 2022, #chunhunghan solution didn't work for me: final RenderBox yourRenderBox = _key.currentContext.findRenderObject(); is the line that breaks my code with: A value of type 'RenderObject?' can't be assigned to a variable of type 'RenderBox'.
So I came up with another approach, inspired by flutter's Widget of the Week video about Positioned, where no Keys are needed, simply a Stack() with two layers: bottom is dynamic-sized, and top is wrapped inside a Positioned.fill() widget that will adapt to the bottom layer size automatically.
Simplified code looks like this:
class MyStackState extends State < MyStack > {
bool _showOverlay = false;
#override
Widget build(BuildContext context) {
// we set up the Stack
return Stack(
children: [
// Content of dynamic size (bottom layer)
Padding(
// your dynamic content here...
),
// Overlay (top layer that adapts to the size of bottom layer)
if (_showOverlay == true)
Positioned.fill(
child: Container(
// your overlay here...
),
),
],
);
}
}
Note that the Positioned.fill() will strictly take the size of the underlying layer, so if the underlying layer is of height 0 or width 0, then nothing will be rendered, not even if the Positioned.fill() had sized content inside, for example in my case I needed a CircularProgressIndicator() there to let the user know something was processing... it didn't show if content was empty.
I edited the DartPad link you provided with a functioning example of this solution, hope it helps! Edited DartPad with Positioned.fill() solution.

How to apply Gradient filter to an Icon?

I need to put a gradient on my Icon.
How do I achieve this?
One way to do this is to use a ShaderMask widget. If you wrap an icon with this, you can apply any gradient you like:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: LinearGradientMask(
child: Icon(
Icons.book,
size: 250,
color: Colors.white,
),
),
),
),
);
}
}
class LinearGradientMask extends StatelessWidget {
LinearGradientMask({this.child});
final Widget child;
#override
Widget build(BuildContext context) {
return ShaderMask(
shaderCallback: (bounds) {
return RadialGradient(
center: Alignment.topLeft,
radius: 1,
colors: [Colors.blue, Colors.red],
tileMode: TileMode.mirror,
).createShader(bounds);
},
child: child,
);
}
}
giving you something like looks like this:
You can also do this:
Container(
height: 50,
width: 50,
child: Icon(Icons.error, color: Colors.white),
decoration: BoxDecoration(gradient: LinearGradient(colors: [Colors.yellow, Colors.black])),
),

Implementing "Mobile step progress bar" from the material guidelines

The material design guidelines used to have a section on steppers: https://material.io/archive/guidelines/components/steppers.html#steppers-types-of-steps . This included various stepper types, including "Mobile step progress bar":
Flutter has a Stepper class, but very scantily documented. How would I implement the kind of stepper seen above?
There's a request for documentation on Github that touches on this topic, but so far, there aren't any clear guides on how to implement this.
I don't think Flutter's stepper class is the same stepper you're talking about. It's meant for a completely different purpose.
As for your "stepper", it's actually quite simple to make one yourself. I've done it two ways - one with a LinearProgressIndicator and the other with a simple gradient, but you could also do it pretty easily with a custompaint.
I've included it within a PageView example as that seems to be what you're doing with it. Some of the PageView code is cribbed from the flutter gallery pagination example and actually that page would probably be worth looking at as well.
import 'package:flutter/material.dart';
main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
PageController _pageController = new PageController(initialPage: 0);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: PageView(
children: <Widget>[
Center(child: Text("Page 1")),
Center(child: Text("Page 2")),
Center(child: Text("Page 3")),
Center(child: Text("Page 4")),
],
controller: _pageController,
physics: AlwaysScrollableScrollPhysics(),
),
bottomNavigationBar: Column(
children: <Widget>[
Container(
height: 10,
margin: EdgeInsets.symmetric(horizontal: 10),
alignment: Alignment.center,
child: GradientPageIndicator(
pageController: _pageController,
pageCount: 4,
primaryColor: Colors.blue,
secondaryColor: Colors.blue.withOpacity(0.2),
),
),
Container(
height: 10,
margin: EdgeInsets.symmetric(horizontal: 10),
alignment: Alignment.center,
child: ProgressPageIndicator(
pageController: _pageController,
pageCount: 4,
primaryColor: Colors.blue,
secondaryColor: Colors.blue.withOpacity(0.2),
),
),
],
mainAxisSize: MainAxisSize.min,
),
),
);
}
}
class ProgressPageIndicator extends AnimatedWidget {
final PageController pageController;
final int pageCount;
final Color primaryColor;
final Color secondaryColor;
final num height;
ProgressPageIndicator({
#required this.pageController,
#required this.pageCount,
#required this.primaryColor,
#required this.secondaryColor,
this.height = 2.0,
}) : super(listenable: pageController);
#override
Widget build(BuildContext context) {
return Container(
height: height,
child: LinearProgressIndicator(
backgroundColor: secondaryColor,
valueColor: Tween(begin: primaryColor, end: primaryColor).animate(kAlwaysCompleteAnimation),
value: (pageController.page ?? pageController.initialPage) / (pageCount - 1),
),
);
}
}
class GradientPageIndicator extends AnimatedWidget {
final PageController pageController;
final int pageCount;
final Color primaryColor;
final Color secondaryColor;
final num height;
GradientPageIndicator({
#required this.pageController,
#required this.pageCount,
#required this.primaryColor,
#required this.secondaryColor,
this.height = 2.0,
}) : super(listenable: pageController);
#override
Widget build(BuildContext context) {
double pagePosition = (pageController.page ?? pageController.initialPage) / (pageCount - 1);
double alignPosition = pagePosition * 2 - 1;
print("PagePosition: $pagePosition, alignPosition: $alignPosition");
return Container(
height: height,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [primaryColor, secondaryColor],
begin: Alignment(alignPosition - 0.0001, 0),
end: Alignment(alignPosition + 0.0001, 0),
),
),
);
}
}