I want to create a rounded image widget, but it ends up pixelated.
With Image.network(url), I get the following:
while the original looks like this:
Here is the relevant code:
class RoundedImage extends StatelessWidget {
final String URL;
final double size;
final bool dynamicallySized;
final double borderRadius;
final bool onlyTopBorderRadius;
const RoundedImage({
#required this.size,
#required this.url,
this.dynamicallySized = false,
this.borderRadius = 8.0,
this.onlyTopBorderRadius = false,
});
#override
Widget build(BuildContext context) {
final newSize = dynamicallySized ? PaddingUtils.getPadding(context, padding: size) : size;
return ClipRRect(
borderRadius:
onlyTopBorderRadius ? BorderRadius.vertical(top: Radius.circular(borderRadius)) : BorderRadius.circular(borderRadius),
child: CachedNetworkImage(
imageUrl: url,
height: newSize,
width: newSize,
fit: BoxFit.cover,
),
);
}
}
Try to add this property to CachedNetworkImage
filterQuality: FilterQuality.high
Related
This question already has answers here:
The default 'List' constructor isn't available when null safety is enabled. Try using a list literal, 'List.filled' or 'List.generate'
(4 answers)
Closed 1 year ago.
i have an error when i write the code
heres the code i wrote
class ProgressHUD extends StatelessWidget {
final Widget child;
final bool inAsyncCall;
final double opacity;
final Color color;
final Animation<Color> valueColor;
ProgressHUD({
required Key key,
required this.child,
required this.inAsyncCall,
this.opacity = 0.3,
this.color = Colors.grey,
required this.valueColor,
}) : super(key: key);
#override
Widget build(BuildContext context) {
List<Widget> widgetList = new List<Widget>();
widgetList.add(child);
if (inAsyncCall) {
final model = new Stack(
children: [
new Opacity(
opacity: opacity,
child: ModalBarrier(dismissible: false, color: color),
),
new Center(
child: new CircularProgressIndicator()
),
],
);
return Stack(children: widgetList,);
}
}
}
The error message that appear is on Build says The body might complete normally, causing 'null' to be returned, but the return type is a potentially non-nullable type.
Then the Second error is in List says 'list' is deprecated and shouldnt be used
can someone help me to fix it thanks before
List<Widget> widgetList = new List<Widget>();
widgetList.add(child);
needs to be
final widgetList = [child];
I want a way to load placeholder image if network/asset image fails to load. here's what i have tried.
class CircularImageWidget extends StatelessWidget {
CircularImageWidget({
Key key,
#required this.radius,
#required this.assetPath,
#required this.imageType,
}) : super(key: key);
final double radius;
final String assetPath;
final ImageType imageType;
Image getErrorImage() {
return Image.asset(AssetUtils.profilepic);
}
ImageProvider returnImageAccordingToImageType() {
if (imageType == ImageType.Network) {
return NetworkImage(
assetPath,
);
} else {
return AssetImage(
assetPath,
);
}
}
#override
Widget build(BuildContext context) {
return CircleAvatar(
backgroundImage: returnImageAccordingToImageType() ??
AssetImage(AssetUtils.profilepic),
radius: radius,
onBackgroundImageError: (exception, stackTrace) {
return AssetImage(AssetUtils.profilepic);
},
);
}
}
You can achieve this by checking the URL of the image and displaying different widgets based on it.
#override
Widget build(BuildContext context) {
return CircleAvatar(
imageURL== null? backgroundImage:
AssetImage(AssetUtils.profilepic),
radius: radius,
):backgroundImage:
NetworkImage(imageURL),
radius: radius,
),
}
In Flutter, How to get the width and height of a imageProvider?
In the example below, I need to get the width of the imageProvider. So that I can calculate the minScale for the PhotoView widget in photo_view package.
import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart';
class ImageViewerScreen extends StatelessWidget {
final ImageProvider imageProvider;
ImageViewerScreen({#required this.imageProvider});
#override
Widget build(BuildContext context) {
final double _screenWidth = MediaQuery.of(context).size.width;
final double _imageWidth =
imageProvider.width; //Here is the question: how to get the width of this imageProvider?
final double _minScale = _screenWidth / _imageWidth;
return Scaffold(
appBar: AppBar(),
body: Container(
child: PhotoView(
imageProvider: imageProvider,
minScale: _minScale,
)),
);
}
}
So I'm trying to create an animation in Flutter that requires a different outcome every time the user presses a button.
I've implemented the following code according to the Flutter Animations tutorial and created a function to update it.
class _RoulettePageWidgetState extends State<RoulettePageWidget>
with SingleTickerProviderStateMixin {
Animation<double> _animation;
Tween<double> _tween;
AnimationController _animationController;
int position = 0;
#override
void initState() {
super.initState();
_animationController =
AnimationController(duration: Duration(seconds: 2), vsync: this);
_tween = Tween(begin: 0.0, end: 100.0);
_animation = _tween.animate(_animationController)
..addListener(() {
setState(() {});
});
}
void setNewPosition(int newPosition) {
_tween = Tween(
begin: 0.0,
end: math.pi*2/25*newPosition);
_animationController.reset();
_tween.animate(_animationController);
_animationController.forward();
}
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: <Widget>[
Center(
child: Transform.rotate(
angle: _animationController.value,
child: Icon(
Icons.arrow_upward,
size: 250.0,
),
)),
Expanded(
child: Container(),
),
RaisedButton(
child: Text('SPIN'),
onPressed: () {
setState(() {
setNewPosition(math.Random().nextInt(25));
});
},
)
],
)
);
}
}
As you can see I'm updating the _tween's begin: and end: but this doesn't seem to change the animation.
So what should I be doing to create a 'different' animation every time the users presses the button?
The general idea is to make the animations build upon each other with a random new value so for example:
first spin: 0 -> 10
second spin: 10 -> 13
third spin: 13 -> 18
... etc
So I wondered if I could update the animation, or should I create a new animation every time?
Another thing I could think of was tracking the positions and use the same animation every time (0.0 -> 100.0) to act as a percentage of the transfer.
So instead of creating a new animation from 10 -> 15 I would be doing something like:
currentValue = 10 + (15-10)/100*_animationController.value
I'm going to skip your code a bit, and focus on what you're really asking:
The general idea is to make the animations build upon each other with a random new value so for example:
first spin: 0 -> 10
second spin: 10 -> 13
third spin: 13 -> 18
... etc
With an explicit animation like this, there are three objects you are interested in:
a controller, which is a special kind of Animation that simply generates values linearly from its lower to its upper bound (both doubles, typically 0.0 and 1.0). You can control the flow of the animation - send it running forward, reverse it, stop it, or reset it.
a tween, which isn't an Animation but rather an Animatable. A tween defines the interpolation between two values, which don't even have to be numbers. It implements a transform method under the hood that takes in the current value of an animation and spits out the actual value you want to work with: another number, a color, a linear gradient, even a whole widget. This is what you should use to generate your angles of rotation.
an animation, which is the animation whose value you're actually going to work with (so this is where you'd grab values to build with). You get this by giving your tween a parent Animation to transform - this might be your controller directly but can also be some other sort of animation you've built on it (like a CurvedAnimation, which would give you easing or bouncy/elastic curves and so on). Flutter's animations are highly composable that way.
Your code is failing largely because you're not actually using the top-level animation you created in your build method and you're creating a new tween and animation every time you call setNewPosition. You can use the same tween and animation for multiple animation "cycles" - simply change the begin and end properties of the existing tween and it bubbles up to the animation. That ends up something like this:
class _RoulettePageWidgetState extends State<RoulettePageWidget>
with SingleTickerProviderStateMixin {
Animation<double> _animation;
Tween<double> _tween;
AnimationController _animationController;
math.Random _random = math.Random();
int position = 0;
double getRandomAngle() {
return math.pi * 2 / 25 * _random.nextInt(25);
}
#override
void initState() {
super.initState();
_animationController =
AnimationController(duration: Duration(seconds: 2), vsync: this);
_tween = Tween(begin: 0.0, end: getRandomAngle());
_animation = _tween.animate(_animationController)
..addListener(() {
setState(() {});
});
}
void setNewPosition() {
_tween.begin = _tween.end;
_animationController.reset();
_tween.end = getRandomAngle();
_animationController.forward();
}
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: <Widget>[
Center(
child: Transform.rotate(
angle: _animation.value,
child: Icon(
Icons.arrow_upward,
size: 250.0,
),
)),
Expanded(
child: Container(),
),
RaisedButton(
child: Text('SPIN'),
onPressed: setNewPosition,
)
],
)
);
}
}
Hope that helps!
While working, in no situation will you actually want to make these animations within your layout as explained by #filleduchaos.
This is under optimized, as you're rebuilding far more than you should for the animation. And it's a pain to write yourself.
You'll want to use the AnimatedWidget family for this. They are divided into two
kinds:
XXTransition
AnimatedXX
The first is a low layer that consumes an Animation and listens to it so that you don't need to do that ugly :
..addListener(() {
setState(() {});
});
The second handles the remaining pieces: AnimationController, TickerProvider and Tween.
This makes using animations much easier as it's almost entirely automatical.
In your case a rotation example would be as followed:
class RotationExample extends StatefulWidget {
final Widget child;
const RotationExample({
Key key,
this.child,
}) : super(key: key);
#override
RotationExampleState createState() {
return new RotationExampleState();
}
}
class RotationExampleState extends State<RotationExample> {
final _random = math.Random();
double rad = 0.0;
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: _rotate,
child: AnimatedTransform(
duration: const Duration(seconds: 1),
alignment: Alignment.center,
transform: Matrix4.rotationZ(rad),
child: Container(
color: Colors.red,
height: 42.0,
width: 42.0,
),
),
);
}
void _rotate() {
setState(() {
rad = math.pi * 2 / 25 * _random.nextInt(25);
});
}
}
Easier right?
The irony is that Flutter forgot to provide an AnimatedTransform (even although we have many others !). But no worries, I made it for you!
The AnimatedTransform implementation is as followed :
class AnimatedTransform extends ImplicitlyAnimatedWidget {
final Matrix4 transform;
final AlignmentGeometry alignment;
final bool transformHitTests;
final Offset origin;
final Widget child;
const AnimatedTransform({
Key key,
#required this.transform,
#required Duration duration,
this.alignment,
this.transformHitTests = true,
this.origin,
this.child,
Curve curve = Curves.linear,
}) : assert(transform != null),
assert(duration != null),
super(
key: key,
duration: duration,
curve: curve,
);
#override
_AnimatedTransformState createState() => _AnimatedTransformState();
}
class _AnimatedTransformState
extends AnimatedWidgetBaseState<AnimatedTransform> {
Matrix4Tween _transform;
#override
void forEachTween(TweenVisitor<dynamic> visitor) {
_transform = visitor(_transform, widget.transform,
(dynamic value) => Matrix4Tween(begin: value));
}
#override
Widget build(BuildContext context) {
return Transform(
alignment: widget.alignment,
transform: _transform.evaluate(animation),
transformHitTests: widget.transformHitTests,
origin: widget.origin,
child: widget.child,
);
}
}
I will submit a pull request so that in the future you won't need this bit of code.
If you want to reverse your animation with a different path (go/back way). Try this.
In your setNewPosition function, just define new begin/end value for _tween.
void setNewPosition() {
_tween.begin = 0; //new begin int value
_tween.end = 1; //new end int value
_animationController.reverse();
}
I'm trying to create a dynamic animation described in the mockup above. What I need is:
A round shape that will represent an avatar (user pic, for example);
A text centered below it.
Below those (that use half of the screen) is a scrollable PageView.
The animation should be as the following:
Begin: In a Stack both centered at the begin.
Animation: Scaling down and sliding the text (with variable lenght) that must be to the right of the avatar.
End: As the second image in the mockup. Side-to-side while the content below keeps scrolling.
Thought in SliverPersistentHeader combined with CustomMultiChildLayout but the problem is that the text starts centered and ends align to the left and I can animate this dynamically. I was trying to remove the offset of the centered text in the end but it doesn't feel right.
Any help or a sample only with this animation would be appreciate. Thank you.
You will need a Sliver to animate your layout based on the scroll offset. More specifically, SliverPersistentHeader in your situation.
CustomMultiChildLayout is not necessary though, you can achieve the same result using tweens and align/padding/stuff. But you can give it a go if your layout starts to become too complex.
The trick is to use the scroll offset given by SliverPersistentHeader to compute the current progression. Then use that progression to position element between their original and final position.
Here's a raw example:
class TransitionAppBar extends StatelessWidget {
final Widget avatar;
final Widget title;
const TransitionAppBar({this.avatar, this.title, Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return SliverPersistentHeader(
pinned: true,
delegate: _TransitionAppBarDelegate(
avatar: avatar,
title: title,
),
);
}
}
class _TransitionAppBarDelegate extends SliverPersistentHeaderDelegate {
final _avatarTween =
SizeTween(begin: Size(150.0, 150.0), end: Size(50.0, 50.0));
final _avatarMarginTween =
EdgeInsetsTween(begin: EdgeInsets.zero, end: EdgeInsets.only(left: 10.0));
final _avatarAlignTween =
AlignmentTween(begin: Alignment.topCenter, end: Alignment.centerLeft);
final _titleMarginTween = EdgeInsetsTween(
begin: EdgeInsets.only(top: 150.0 + 5.0),
end: EdgeInsets.only(left: 10.0 + 50.0 + 5.0));
final _titleAlignTween =
AlignmentTween(begin: Alignment.center, end: Alignment.centerLeft);
final Widget avatar;
final Widget title;
_TransitionAppBarDelegate({this.avatar, this.title})
: assert(avatar != null),
assert(title != null);
#override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
final progress = shrinkOffset / 200.0;
final avatarSize = _avatarTween.lerp(progress);
final avatarMargin = _avatarMarginTween.lerp(progress);
final avatarAlign = _avatarAlignTween.lerp(progress);
final titleMargin = _titleMarginTween.lerp(progress);
final titleAlign = _titleAlignTween.lerp(progress);
return Stack(
fit: StackFit.expand,
children: <Widget>[
Padding(
padding: avatarMargin,
child: Align(
alignment: avatarAlign,
child: SizedBox.fromSize(size: avatarSize, child: avatar),
),
),
Padding(
padding: titleMargin,
child: Align(
alignment: titleAlign,
child: DefaultTextStyle(
style: Theme.of(context).textTheme.title, child: title),
),
)
],
);
}
#override
double get maxExtent => 200.0;
#override
double get minExtent => 100.0;
#override
bool shouldRebuild(_TransitionAppBarDelegate oldDelegate) {
return avatar != oldDelegate.avatar || title != oldDelegate.title;
}
}
which you can use with a CustomScrollView:
Scaffold(
body: CustomScrollView(
slivers: <Widget>[
TransitionAppBar(
avatar: Material(
color: Colors.blue,
elevation: 3.0,
),
title: Text("Hello World"),
),
SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return ListTile(
title: Text('$index'),
);
}),
)
],
),
);