I want to use Hero to animate the AnimatedIcon in the process of route navigation. What should I do?
_controller = AnimationController(
duration: Duration(microseconds: 1000), value: 1.0, vsync: this);
void _toggleFrontLayer() {
final AnimationStatus status = _controller.status;
final bool isOpen = status == AnimationStatus.completed ||
status == AnimationStatus.forward;
_controller.fling(velocity: isOpen ? -2.0 : 2.0);
}
leading: Hero(
child: Material(
color: Colors.transparent,
child: IconButton(
icon: AnimatedIcon(
icon: AnimatedIcons.menu_arrow,
progress: _controller,
),
onPressed: _toggleFrontLayer,
),
),
tag: "leading",
),
leading: Hero(
child: Material(
color: Colors.transparent,
child: IconButton(
icon: AnimatedIcon(
icon: AnimatedIcons.menu_arrow,
progress: _controller,
),
onPressed: () {
Navigator.of(context).pop();
},
),
),
tag: "leading",
),
The appbar looks like this:now
And the AnimatedIcon's animation looks like this:AnimatedIcon
What I want is showing the animation of the AnimatedIcon in the process of route navigation instead of fade in and fade out or other animations.
First things first. If you don't have a Material ancestor widget, you're probably getting an exception while you're routing between screens. To prevent it, you can wrap your IconButton in a Material with the property color: Colors.transparent.
Second, there is nothing wrong with using Hero to animate the leading button between screens, however, you won't notice a difference since the position is exactly the same and there is nothing to animate from-to. The result is something like this:
However, if you actually change the position of the IconButton between screens, you will get the Hero animation as expected.
Related
This should be pretty basic, but I can't seem to figure out how to disable or ignore parent touches when a child IconButton is pressed. I am working on a treeview that can expand and collapse. When I click the row (a Card), the children will be shown or not (which is working). The problem I have is that when touching the child IconButton, both the IconButton's onPressed event as well as the Listener of the Card's onPointerDown event. But I only want the IconButton's onPressed event to trigger. Any suggestions?
The listener for the card (row):
Listener(
behavior: HitTestBehavior.deferToChild,
onPointerDown: (PointerDownEvent event) {
toggleExpanded();
print("row pressed");
},
child: widget.parent
),
The IconButton:
return Card(
color: Theme.of(context).colorScheme.surfaceVariant,
elevation: 2.0,
child: ListTile(
leading: Icon(Icons.folder, color: Colors.amber),
title: titleWidget,
subtitle: countWidget,
//trailing: expandButton,
trailing:
IconButton(
icon: Icon(Icons.more_vert), onPressed: () { print("more icon"); },
),
),
);
Use GestureDetector instead
GestureDetector(
onTap: () { print("row pressed"); },
child: <widget>
),
i want to rescale my icon and rotate my icon until it's scale become 0,but i can only 'set' icon's size which means size become instantly 0 without animation, tried to wrap icon with animated container, but it doesn't effect to icon, any ideas for rotating and rescaling icon by time ? my code's here below.
Widget build1(BuildContext context) {
Widget widget1;
var animatedContainer = AnimatedContainer(
width: double.infinity,
height: double.infinity,
duration: Duration(seconds: 2),
color: animSize ? Colors.greenAccent[700] : Colors.yellow,
child: IconButton(
icon: Icon(
Icons.group,
size: animSize ? 0:90,
),
onPressed: () {
setState(() {
animSize = true;
});
},
));
widget1 = animatedContainer;
return widget1;
}
I have a Future which returns a List and using FutureBuilder and inside it a ListView.builder I show data that I got from Future.
But the problem is, whenever Future returns data my list of widgets just pops directly into the screen. I want to give it a animation (fade or curve) which I am not supposed to trigger manually, instead I want that when I got the data from Future my list to pop to screen not direcly but with some latency/fade/curve or any other animation.
How can I achieve that?
You can use a opacity animation, here you can see an example.
Then you can set
bool _visible = true; //Put this inside your initState
AnimatedOpacity(
opacity: _visible ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child:ListView.builder(
itemCount: count,
padding: EdgeInsets.all(10.0),
itemBuilder: (BuildContext context, int position) {
return Card(
color: Colors.white,
elevation: 2.0,
child: ListTile(
title: Text(this.list[position].name),
subtitle: Text(this.list[position].type),
trailing: GestureDetector(
child: Icon(Icons.delete, color: Colors.grey,),
onTap: () {
_delete(context, list[position]);
},
),
onTap: () {
debugPrint("ListTile Tapped");
navigateToDetail(this.list[position]);
},
),
);
},
)
),
I'm currently trying to implement a radial menu appearing when the floating action button in the bottom navigation menu is clicked (image1). The animation and rendering works fine but after the animation, the on pressed button function is no even triggered, when the buttons get clicked. I already read that this is due to the stack, wrapping the buttons and the animation. The area of the transformed buttons is not defined a priori. This causes gesture detection of the Buttons to stay behind the FAB. Wrapping the Stack with a fixed size container solves the problem, however, it totally breaks the layout. Also, the clickable navigations icons in the background are not reachable as the container is laying above the navbar (see image2). Is there a known workaround to fix this problem? I am really looking forward to getting some professional help. :)
Image of not working buttons and desired layout
Image of the broken UI after wrapping the stack with a container. But in this solution button pressed is working
The code is attached here:
import 'package:flutter/material.dart';
import 'dart:math';
import 'package:vector_math/vector_math.dart' show radians;
class RadialFloatingActionButton extends StatefulWidget {
#override
_RadialFloatingActionButtonState createState() => _RadialFloatingActionButtonState();
}
class _RadialFloatingActionButtonState extends State<RadialFloatingActionButton> with
SingleTickerProviderStateMixin {
AnimationController controller;
#override
void initState() {
super.initState();
controller = AnimationController(
duration: Duration(milliseconds: 1100), vsync: this);
}
#override
Widget build(BuildContext context) {
return RadialAnimation(controller: controller);
}
}
class RadialAnimation extends StatelessWidget {
RadialAnimation({Key key, this.controller})
: scale = Tween<double>(
begin: 1.5,
end: 0.0,
).animate(
CurvedAnimation(parent: controller, curve: Curves.elasticInOut),
),
translation = Tween<double>(
begin: 0.0,
end: 90.0,
).animate(
CurvedAnimation(parent: controller, curve: Curves.easeInOutCirc),
),
super(key: key);
final AnimationController controller;
final Animation<double> scale;
final Animation<double> translation;
build(context) {
return AnimatedBuilder(
animation: controller,
builder: (context, builder) {
return Stack(alignment: Alignment.center, children: [
_buildButton(200, color: Colors.black, emoji: "A"),
_buildButton(245, color: Colors.black, emoji: "B"),
_buildButton(295, color: Colors.black, emoji: "C"),
_buildButtonMore(340, color: Colors.grey),
Transform.scale(
scale: scale.value -
1.5, // subtract the beginning value to run the opposite animation
child: FloatingActionButton(
child: Icon(
Icons.close,
),
onPressed: _close,
backgroundColor: Colors.black),
),
Transform.scale(
scale: scale.value,
child: FloatingActionButton(
child: Icon(
Icons.people,
color: Colors.white,
),
onPressed: _open,
backgroundColor: Colors.black),
)
]);
});
}
_buildButtonMore(double angle, {Color color}) {
final double rad = radians(angle);
return Transform(
transform: Matrix4.identity()
..translate(
(translation.value) * cos(rad), (translation.value) * sin(rad)),
child: FloatingActionButton(
child: Icon(Icons.more_horiz),
backgroundColor: color,
onPressed: _close,
elevation: 0));
}
_buildButton(double angle, {Color color, String emoji}) {
final double rad = radians(angle);
return Transform(
transform: Matrix4.identity()
..translate(
(translation.value) * cos(rad), (translation.value) * sin(rad)),
child: FloatingActionButton(
child: Center(
child: new Text(
emoji,
style: TextStyle(fontSize: 30),
textAlign: TextAlign.center,
),
),
backgroundColor: color,
onPressed: _close,
elevation: 0));
}
_open() {
controller.forward();
}
_close() {
controller.reverse();
}
}
I am not able to test right now, but wrapping in a container seems like the proper workaround, as Transforms seems to not apply to hit test behaviors in unbounding boxes (thats why Container makes it work). Maybe there’s a way (some sort of widget or property) that allows hitTestBehavior like GestureDetector where you can set it to “opaque areas” instead the entire bounding box of the Container capturing the touches.
Sorry cause i cant provide you an answer directly, will check it out later on the computer :)
In my code I call a bottom sheet to display a list of tiles. These tiles contain buttons that display a snackbar. That functionality works fine, the only issue is that the snackbar is displayed behind the bottom sheet so you can only see it if you close the bottom sheet. Each of them are called with the following code:
1. Bottom Sheet:
void _settingModalBottomSheet(context, stream, scaffoldKey ) {
if (_availableRides.length == 0) {
return null;
} else {
return scaffoldKey.currentState.showBottomSheet((context) {
return Column(
children: Widgets;
});
}
}
2. Snackbar
widget.scaffoldKey.currentState.showSnackBar(SnackBar(
content: Text("Created", textAlign:
TextAlign.center,),),
Does anyone know how I can position the snackbar in front of the bottom sheet
So I was able to solve this by just adding another Scaffold() to my Bottom sheet and passing it a new scaffold key
SnackBar has a property for this. It's called behavior, you could do this:
SnackBar(
behavior: SnackBarBehavior.floating,
...
SnackBarBehavior enum
floating → const SnackBarBehavior
This behavior will cause SnackBar to be shown above other widgets in
the Scaffold. This includes being displayed above a
BottomNavigationBar and a FloatingActionButton.
See material.io/design/components/snackbars.html for more details.
I solved by Set (padding from bottom to SnackBar) As much as the height of the (bottomSheet : height) .
In This Case I Have This bottomSheet:
bottomSheet: Container(
child: RaisedButton(...),
width: MediaQuery.of(context).size.width,
height: AppBar().preferredSize.height * 0.85,
),
And This snackBar:
_scaffoldKey.currentState.showSnackBar(SnackBar(
padding:EdgeInsetsDirectional.only(
bottom:AppBar().preferredSize.height * 0.85),
backgroundColor: Colors.red,
duration: new Duration(milliseconds: 3000),
content: Text('ETC..'),
));
You can achieve this Simply by wrapping your BottomSheet widget with a Scaffold.
eg:
void _settingModalBottomSheet(context, stream, scaffoldKey ) {
if (_availableRides.length == 0) {
return null;
} else {
return scaffoldKey.currentState.showBottomSheet((context) {
return Scaffold(
body: Column(
children: Widgets;
})
);
}
}
I arrived on pretty decent solution. I wrapped my bottom sheet in a Scaffold, but I added it as the bottomSheet parameter. While adding the Scaffold, some trailing background will be added, so I just made its background transparent.
Scaffold(
backgroundColor: Colors.transparent,
bottomSheet: ...,
)
This is a working solution according to documentation.
https://docs.flutter.dev/cookbook/design/snackbars
This example works with bottom sheets as well.
Initialize ScaffoldMessengerKey.
Wrap your component widget with Scaffold.
Wrap Scaffold with ScaffoldMessenger.
Add key scaffoldMessengerKey to ScaffoldMessenger
Call method scaffoldMessengerKey.currentState?.showSnackBar(SnackBar());
Example:
final scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
// Any widget with button.
// (Bottom sheet also) - root widget must be ScaffoldMessenger.
ScaffoldMessenger(
key: scaffoldMessengerKey,
child: Scaffold(
body: Container(
child: ElevatedButton(
style: raisedButtonStyle,
onPressed: () {
scaffoldMessengerKey.currentState?.showSnackBar(
//SnackBar design.
SnackBar(
backgroundColor: Colors.white,
elevation: 8,
content: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(5),
),
child: Text(
'Simple snackbar text.',
style: FlutterFlowTheme.of(context).bodyText1
.override(fontFamily: 'Rubik',
fontWeight: FontWeight.w300,
lineHeight: 1.5,
),
),
action: SnackBarAction(
label: 'Undo',
onPressed: () {
// Some code to undo the change.
},
),
duration: Duration(seconds: 5),
behavior: SnackBarBehavior.floating,
},
child: Text('Open snackbar over bottom sheet!'),
); //ElevatedButton
); //Container
); //Scaffold
); //ScaffoldMessenger
Note:
With this approach you don't need to pass BuildContext.
If you don't want to register ScaffoldMessengerKey.
You can show SnackBar like this: ScaffoldMessenger.of(context).showSnackBar(SnackBar());
I solved this by changing bottomSheet to bottomNavigationBar since the floating snack bar solution didn't work for me.
you can use flushbar package. I think this is the better option if need to use with bottomSheet.
context should be your page's context, not bottomsheet context
any event inside bottomSheet
CustomFlushBar().flushBar(text: 'Thank you for your payment!', context: context,duration: 2);
CustomFlushBar class
class CustomFlushBar {
void flushBar({int duration, #required String text, Color iconColor, IconData iconData, Color backgroundColor, #required BuildContext context}) async {
await dismiss();
Flushbar(
margin: EdgeInsets.all(8),
borderRadius: 8,
backgroundColor: backgroundColor ?? Palette.greenButton,
icon: Icon(iconData ?? Icons.done, color: iconColor ?? Palette.white),
flushbarStyle: FlushbarStyle.FLOATING,
message: text,
duration: Duration(seconds: duration ?? 3),
)..show(context);
}
Future<void> dismiss() async {
if (!Flushbar().isDismissed()) {
await Flushbar().dismiss();
}
}
}