I have two widgets,
Widget FirstWidget()
{
return Container(
height: 40,
child: new Text('Food Products',style: TextStyle(color: Colors.gray))
decoration: new BoxDecoration(
borderRadius: new BorderRadius.all(new Radius.circular(4.0)),
border: Border.all(width: 2, color:Colors.gray)),
padding: new EdgeInsets.all(12.0),
)
}
Widget SecondWidget()
{
return Container(
height: 40,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Food Products',
TextStyle(color: Colors.blue),
SizedBox(width:8),
Icon(Icons.check,color:Colors.blue ,size: 24,)
],),
decoration: new BoxDecoration(
borderRadius: new BorderRadius.all(new Radius.circular(4.0)),
border: Border.all(width: 2, color:Colors.red)),
padding: new EdgeInsets.all(12.0),
)
}
Now, i need to animate the two widgets based on selection,
class BasicProduct extends StatefulWidget {
final String name;
final bool isSelected;
BasicProduct({Key key, #required this.name, this.isSelected = false});
#override
State<StatefulWidget> createState() => _BasicProduct(name, isSelected);
}
class _BasicProduct extends State<BasicProduct> {
final String name;
bool _isSelected;
_BasicProduct(this.name, this._isSelected);
#override
Widget build(BuildContext context) {
Widget _myAnimatedWidget = _isSelected ? SecondWidget() : FirstWidget();
return new GestureDetector(
onTap: () {
setState(() {
_isSelected = !_isSelected;
_myAnimatedWidget =
_isSelected
? SecondWidget()
: FirstWidget();
});
},
child: AnimatedContainer(
key : _animatedContainerKey
duration: const Duration(seconds: 1),
width: boxWidth
child: _myAnimatedWidget
);
}
}
How to get the width for the two widgets to animate?
Tried Calling WidgetsBinding.instance.addPostFrameCallback((_)=> getSize) in the initState to get the Size
getSize()
{
var boxWidth = _animatedContainerKey.currentContext.findRenderObject().size.width;
}
This Size returning for the SecondWidget only, when firstWidget is changed, the Size returning for SecondWidget only.
Can anyone help how to resolve the issue?
I would take a different approach to this. For one, if you can, you should try using the AnimatedCrossFade widget - it automatically handles both the fade and the size change between two different widgets. If you can make it so that instead of having to specify an exact size in your widgets (always a bad idea) they simply fit to the available space, then this should do what you want. However, without more details of what you're actually trying to accomplish with the animation & size of the widgets, it's a bit difficult to help.
If this doesn't work, then you should consider using an AnimationController and a widget inheriting AnimatedWidget. This way, you have complete control over the build process of your two widgets - you can size, position, and fade them however you want with a stack + opacity or whatever else works best for you.
I would normally write out an example but I think it's a bit beyond the scope of this answer, however you can find out an example of an animated widget here: https://api.flutter.dev/flutter/widgets/AnimatedWidget-class.html. If you do need to go down this route and can't figure out, it would help if you could add more details about what it is you're trying to accomplish instead of what you've attempted to do to accomplish it.
Related
I need to create a custom widget. In the image there is two circles and in the circles there must be some other widgets.
I tried to use Stack and pray for it to be responsive but it didn't. Any ideas?
Custom widget in Flutter is built for Widget Reusability in App. Custom widgets in Flutter can be of many types Widgets are built to make your code clean and Shorter. It makes the Customization of Code Simple and Easy
// code
** Simple Button Custom Widget **
import 'package:flutter/material.dart';
class button extends StatelessWidget {
button(
{super.key,
required this.buttonfun,
required this.iconData,
required this.textdata});
IconData iconData;
String textdata;
VoidCallback buttonfun;
#override
Widget build(BuildContext context) {
return ElevatedButton.icon(
onPressed: buttonfun, icon: Icon(iconData), label: Text(textdata));
}
}
Try using BoxShape.circle,
Container(
width: 100,
height: 100,
decoration: BoxDecoration(
border: Border.all(width: 3),
shape: BoxShape.circle,
// You can use like this way or like the below line
//borderRadius: new BorderRadius.circular(30.0),
color: Colors.amber,
),
child:Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('ABC'),
Text('XYZ'),
Text('LOL'),
],
),
),
To achieve this in general I would recommend using a Column and then 3 Containers, one for each Circle, and something that draws the in-between part.
I am having an issue when trying to blur a container in a Row widget when the user hovers over it (on Chrome). I have a custom widget called PanelLink, which is supposed to shrink in size and blur the background when the cursor goes over it. It works, but for some reason when hovering over one it also blurs every widget to the left not just itself.
FrontPage.dart:
import 'package:flutter/material.dart';
import 'package:website_v2/CustomWidgets/PanelLink.dart';
class FrontPage extends StatefulWidget {
FrontPage();
#override
_FrontPageState createState() => _FrontPageState();
}
class _FrontPageState extends State<FrontPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Website'),
centerTitle: false,
backgroundColor: Colors.blue[900],
),
backgroundColor: Colors.blueGrey[900],
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
PanelLink(false, 'assets/education.jpg'),
PanelLink(true, 'assets/about.jpeg'),
PanelLink(false, 'assets/projects2.jpg'),
],
),
),
);
}
}
PanelLink.dart:
import 'package:flutter/material.dart';
import 'dart:ui';
class PanelLink extends StatefulWidget {
PanelLink(this._blur, this.imageLoc);
final bool _blur;
final String imageLoc;
#override
PanelLinkState createState() => PanelLinkState(_blur, imageLoc);
}
class PanelLinkState extends State<PanelLink> {
PanelLinkState(this._blur, this.imageLoc);
bool isHovering = false;
bool _blur;
String imageLoc;
Widget build(BuildContext context) {
return Expanded(
child: InkWell(
onTap: () => null,
onHover: (hovering) {
setState(() => {isHovering = hovering});
},
child: Stack(children: [
AnimatedContainer(
duration: const Duration(milliseconds: 1000),
curve: Curves.ease,
margin: EdgeInsets.all(isHovering ? 20 : 0),
decoration: BoxDecoration(
color: Colors.black,
image: DecorationImage(
image: AssetImage(imageLoc), fit: BoxFit.cover)),
child: BackdropFilter(
filter: ImageFilter.blur(
sigmaX: isHovering ? 5 : 0, sigmaY: isHovering ? 5 : 0),
child: Container(
color: Colors.black.withOpacity(0.1),
)),
),
Text('Test')
]),
));
}
}
Here is a picture of the issue occurring. I am hovering the mouse over panel 2, but both panel 1 and 2 are blurred but not panel 3. If I hover over panel 1 only panel 1 is blurred. If I hover over panel 3, all of them are blurred.
I experimented by only making panel two have the BackdropFilter Widget, but panel 1 still got blurred when hovering over panel 2.
Since your using AnimatedContainer widget the problem might be it cant identify the difference between his child widget. Try adding a key value identifier
AnimatedContainer(
child: Container(
key: ValueKey("imageA"), //make sure that this is different on every widget, its better to passed some value here thats different on the other refactored widget
Try experimenting it might work for you
),
),
I'm trying to make a custom drop-down menu in Flutter that is just a simple button that on tap shows a scrollable row of products. I've attached some images that better show what I'm referring to. I tried to use an IconButton and an AnimatedContainer but I can't seem to get it working. I was hoping someone would have a better idea on how to do something like this.
Here is my code so far by the way:
class ModelsDropdown extends StatefulWidget {
final List<Phone> phones;
ModelsDropdown({#required this.phones});
#override
_ModelsDropdownState createState() => _ModelsDropdownState();
}
class _ModelsDropdownState extends State<ModelsDropdown> {
bool _droppedDown;
#override
void initState() {
_droppedDown = false;
super.initState();
}
#override
Widget build(BuildContext context) {
return AnimatedContainer(
duration: Duration(milliseconds: 300),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100.0),
border: Border.all(width: 2.0)
),
width: 32.0,
height: 32.0,
child: Column(
children: [
IconButton(
padding: const EdgeInsets.only(right: 0.0),
icon: Icon(
_droppedDown ? Icons.expand_more : Icons.expand_less
),
color: Colors.black,
onPressed: () {
setState(() {
_droppedDown = !_droppedDown;
});
}
),
_droppedDown ?
Container(
width: MediaQuery.of(context).size.width,
height: 300.0,
color: Colors.orangeAccent,
) :
SizedBox.shrink()
],
),
);
}
}
With the container at the bottom, it doesn't even work. I get an overflow error. Any help is very much appreciated. Thanks a lot, everyone.
I Think you should Change The Below Container with an AnimatedCrossFade.
Animated Cross Fade Has Two Child First and Second ...
https://api.flutter.dev/flutter/widgets/AnimatedCrossFade-class.html
Dont set Height for Your Child ... its Works Perfectly...
AnimatedCrossFade(
duration: const Duration(seconds: 3),
firstChild: Container(), // When you don't want to show menu..
secondChild: menu,
crossFadeState: _first ? CrossFadeState.showFirst : CrossFadeState.showSecond,
)
with this method you don't need Animated Container at top ... remove that.(return Column)...
with Animated cross Fade You don't need to Know The Height of widget and its for works dynamic Heights
-------------- if You Want to use Your Code and Use Fixed Height ----------------
width: _droppedDown ? YourWidth : 32.0,
height: _droppedDown ? 300 : 32.0,
I'm using Transforms in Flutter to create a scrolling carousel for selecting from various options.
This uses standard elements such as ListView.builder, which all works fine, aside from the fact that the parent widget of the Transform doesn't scale down to fit the content as seen here:
Here's the code used to generate the 'card' (there was actually a Card in there, but I've stripped it out in an attempt to get everything to scale correctly):
return Align(
child: Transform(
alignment: Alignment.center,
transform: mat,
child: Container(
height: 220,
color: color,
width: MediaQuery.of(context).size.width * 0.7,
child: Text(
offset.toString(),
style: TextStyle(color: Colors.white, fontSize: 12.0),
),
),
),
);
}
Even if I remove the 'height' parameter of the Container (so everything scales to fit the 'Text' widget), the boxes containing the Transform widgets still have the gaps around them.
Flutter doesn't seem to have any documentation to show how to re-scale the parent if the object within is transformed - anyone here knows or has any idea of a workaround?
EDIT: The widget returned from this is used within a build widget in a Stateful widget. The stack is Column > Container > ListView.builder.
If I remove the Transform, the Containers fit together as I'd like - it seems that performing a perspective transform on the Container 'shrinks' it's content (in this case, the color - check the linked screen grab), but doesn't re-scale the Container itself, which is what I'm trying to achieve.
I have a tricky solution for this: addPostFrameCallback + overlay.
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
// ignore: must_be_immutable
class ChildSizeWidget extends HookWidget {
final Widget Function(BuildContext context, Widget child, Size size) builder;
final Widget child;
final GlobalKey _key = GlobalKey();
OverlayEntry _overlay;
ChildSizeWidget({ this.child, this.builder });
#override
Widget build(BuildContext context) {
final size = useState<Size>(null);
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timestamp) {
_overlay = OverlayEntry(
builder: (context) => Opacity(
child: SingleChildScrollView(
child: Container(
child: child,
key: _key,
),
),
opacity: 0.0,
),
);
Overlay.of(context).insert(_overlay);
WidgetsBinding.instance.addPostFrameCallback((timestamp) {
size.value = _key.currentContext.size;
_overlay.remove();
});
});
return () => null;
}, [child]);
if (size == null || size.value == null) {
return child;
} else {
return builder(context, child, size.value);
}
}
}
Usage:
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
class HomeView extends HookWidget {
#override
Widget build(BuildContext context) {
final change = useState<bool>(false);
final normal = Container(
color: Colors.blueAccent,
height: 200.0,
width: 200.0,
);
final big = Container(
color: Colors.redAccent,
height: 300.0,
width: 200.0,
);
return Column(
children: [
Container(
alignment: Alignment.center,
child: ChildSizeWidget(
child: change.value ? big : normal,
builder: (context, child, size) => AnimatedContainer(
alignment: Alignment.center,
child: SingleChildScrollView(child: child),
duration: Duration(milliseconds: 250),
height: size.height,
),
),
color: Colors.grey,
),
FlatButton(
child: Text('Toggle child'),
onPressed: () => change.value = !change.value,
color: Colors.green,
),
],
);
}
}
I have a menu with several options, they have different height and with the help of the animations this is ok, it's working really nice for me.
Why are you using Align, as much as I can see in your code, there is no property set or used, to align anything. So try removing Align widget around Transform.
Because according to the documentation, Transform is such a widget that tries to be the same size as their children. So that would satisfy your requirement.
For more info check out this documentation: https://flutter.dev/docs/development/ui/layout/box-constraints
I hope it helps!
The ViewFlipper on Android has very a simple API that allows you to show a view from a list of children at a time. You're also able to know what view is being shown to the user programmatically, as well as set up an animation between its transitions, is there a widget that does this similarly in Flutter?
Below is an excerpt from the ViewFlipper documentation.
Simple ViewAnimator that will animate between two or more views that
have been added to it. Only one child is shown at a time. If
requested, can automatically flip between each child at a regular
interval.
A simple way to swap between 2 views, is the AnimatedCrossFade.
A widget that cross-fades between two given children and animates
itself between their sizes.
This widget is an enhanced AnimatedSwitcher and is similar to the ViewFlipper because it takes charge of swapping between 2 views depending on a state by interpolating its sizes and opacities.
This is an example of a simple AnimatedCrossFade that switches between 2 Containers of different colors and sizes.
class MySimpleWidget extends StatefulWidget {
MySimpleWidget({Key key, this.title}) : super(key: key);
final String title;
#override
_MySimpleWidgetState createState() => _MySimpleWidgetState();
}
class _MySimpleWidgetState extends State<MySimpleWidget> {
bool _showFirst = false;
void _swapWidget() {
setState(() {
_showFirst = !_showFirst;
});
}
#override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedCrossFade(
crossFadeState: _showFirst ? CrossFadeState.showFirst : CrossFadeState.showSecond,
firstChild: Container(color: Colors.red, width: 100, height: 50),
secondChild: Container(color: Colors.blue, width: 100, height: 100),
duration: const Duration(milliseconds: 500),
),
MaterialButton(
onPressed: _swapWidget,
child: const Text('Swap widget'),
)
],
),
);
}
}
Its limitation is that it can only crossfade between 2 children, an alternative for 3 or more could be the PageView.