Create a slide effect from top to bottom - dart

I'm looking for a solution where i can hide a widget and only when some button is pressed the visual effect should be the container dropping from top to bottom.
Example: by pressing the action button a container is shown animated from top to bottom. This container is above everything.
Currently my Widget Build structure is the following:
Container
Stack
TheBlueContainerHere()
Flex
Flexible
ListView Builder
I'm not sure if this is the right structure to proceed, but i have not yet figure out a better one.
How can i build something like this?

Solved.
By following this example flutter notify from top of the screen i was able to create what i wanted.
When the action button is pressed:
Navigator.push(
context,
PageRouteBuilder(
opaque: false,
pageBuilder: (BuildContext context, _, __) {
return FunkyNotification();
},
),
);
And the respective code of FunkyNotification() is:
class FunkyNotification extends StatefulWidget {
#override
State<StatefulWidget> createState() => FunkyNotificationState();
}
class FunkyNotificationState extends State<FunkyNotification>
with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<Offset> position;
#override
void initState() {
super.initState();
controller =
AnimationController(vsync: this, duration: Duration(milliseconds: 750));
position = Tween<Offset>(begin: Offset(0.0, -4.0), end: Offset.zero)
.animate(CurvedAnimation(parent: controller, curve: Curves.decelerate));
controller.forward();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Material(
color: Colors.transparent,
child: Align(
alignment: Alignment.topCenter,
child: Padding(
padding: EdgeInsets.only(top: 56.0),
child: SlideTransition(
position: position,
child: Container(
height: 200,
width: double.infinity,
margin: EdgeInsets.only(left: 10, right: 10),
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(5),
bottomRight: Radius.circular(5),
),
),
),
),
),
),
),
);
}
}

Related

Flutter Padding on CupertinoScrollbar - how to prevent row from disappearing as soon as they enter the padding area?

My actual app is a lot more complex but this is a simpler example which can reproduce this issue.
My CupertinoScrollbar is inside a Padding widget with top padding of 300. Ideally, I want the rows in the List to scroll up the entire "padding area" before disappearing. However, as you notice, the rows disappear as soon as it goes into the padding area.
Even though I have defined clipBehavior: Clip.none, it still disappears as soon as it enters the padding area.
If I remove the clipBehavior: Clip.none, then the row is simply clipped as soon as it enters the padding area instead of entirely being removed after it goes out.
How can I make sure the rows don't get removed UNTIL they go outside the padding (in this case, the top edge of screen)?
I think the clipBehavior: Clip.none should only clip items which are outside the bounds of the Widget and should take padding into account.
On iOS, UITableView's contentInset property let me achieve this.
Here's my code:
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
ScrollController scrollController = ScrollController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.only(top: 300),
child: CupertinoScrollbar(
controller: scrollController,
child: CustomScrollView(
clipBehavior: Clip.none,
controller: scrollController,
slivers: [
SliverList(
delegate: SliverChildListDelegate(
List.generate(words.length, (index) {
return Padding(
padding: const EdgeInsets.only(left: 8.0, right: 8.0),
child: Card(
child: ListTile(
title: Text("$index. ${words[index]}"),
onTap: () {
setState(() {
words.removeAt(index);
});
},
),
),
);
}),
),
),
],
),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(
Icons.add_rounded,
size: 35,
color: Colors.white,
),
onPressed: () {
setState(() {
addWords(howMany: 1);
});
},
),
);
}
}
You can use index to solve this problem,
remove the padding above and use like this:
List.generate(words.length, (index) {
if (index == 0) {
return Padding(
padding: EdgeInsets.only(top: 300),
);
}
index = index - 1; // remember to minus the index too
...
}

How to show custom toast dialog in flutter?

I want to show a custom toast (my own widget layout). I know how to show a custom alert dialogue, but that's not what I want.
Because, Alert dialogue:
Has a black background
Prevents touches when it's shown
Has to dismiss manually
I don't want to use flutter toast library because I can't make a custom layout with that.
I want to show my own layout on top of all other widgets and make it disappear after some time. Also, it should not prevent any input when it's shown.
You can add this library to add and customize your own toasts.
Widget widget = Center(
child: ClipRRect(
borderRadius: BorderRadius.circular(30.0),
child: Container(
width: 40.0,
height: 40.0,
color: Colors.grey.withOpacity(0.3),
child: Icon(
Icons.add,
size: 30.0,
color: Colors.green,
),
),
),
);
ToastFuture toastFuture = showToastWidget(
widget,
duration: Duration(seconds: 3),
onDismiss: () {
print("the toast dismiss"); // the method will be called on toast dismiss.
},
);
custom toast
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class Toast {
static void show(
String msg,
BuildContext context) {
Color textColor = Colors.white;
Color backgroundColor = Colors.blueAccent;
dismiss();
Toast._createView(msg, context, backgroundColor, textColor);
}
static OverlayEntry _overlayEntry;
static bool isVisible = false;
static void _createView(
String msg,
BuildContext context,
Color background,
Color textColor,
) async {
var overlayState = Overlay.of(context);
final themeData = Theme.of(context);
_overlayEntry = new OverlayEntry(
builder: (BuildContext context) => _ToastAnimatedWidget(
child: Container(
width: MediaQuery.of(context).size.width,
child: Container(
alignment: Alignment.center,
width: MediaQuery.of(context).size.width,
child: Container(
decoration: BoxDecoration(
color: background,
borderRadius: BorderRadius.circular(20),
),
margin: EdgeInsets.symmetric(horizontal: 20),
padding: EdgeInsets.fromLTRB(16, 10, 16, 10),
child: Text(
msg,
softWrap: true,
style: themeData.textTheme.body1.copyWith(
fontFamily: 'intel',
color: Colors.white,
),
),
),
),
),
),
);
isVisible = true;
overlayState.insert(_overlayEntry);
}
static dismiss() async {
if (!isVisible) {
return;
}
isVisible = false;
_overlayEntry?.remove();
}
}
class _ToastAnimatedWidget extends StatefulWidget {
_ToastAnimatedWidget({
Key key,
#required this.child,
}) : super(key: key);
final Widget child;
#override
_ToastWidgetState createState() => _ToastWidgetState();
}
class _ToastWidgetState extends State<_ToastAnimatedWidget>
with SingleTickerProviderStateMixin {
bool get _isVisible => true; //update this value later
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Positioned(
bottom: 50,
child: AnimatedOpacity(
duration: Duration(seconds: 2),
opacity: _isVisible ? 1.0 : 0.0,
child: widget.child,
)
);
}
}
for call
Toast.show(ApiContent.something_wrong, context);

How to animate Flutter layout on keyboard appearance

I'm building a Flutter app, mainly for iOS.
One of my views has a text field, and iOS keyboard appears when you tap on it. The problem is - layout does not change smoothly like it does in native iOS apps. Instead, it instantly jumps to the final available screen height even before keyboard opening animation finishes.
I tried wrapping my SafeArea element in AnimatedSize and AnimatedContainer - it didn't help.
My layout code:
SafeArea(child:
Column(children:[
TextField(...)
])
)
How can I make the layout resize smoothly when the keyboard appears?
Expected:
Actual
I use something like that:
AnimatedPadding(
padding: MediaQuery.of(context).viewInsets,
duration: const Duration(milliseconds: 100),
curve: Curves.decelerate,
child: ....
)
This animates the padding based on the viewInsets(software keyboard height).
The desired output can be achieved using AnimatedPadding Widget, though this is not perfect, but better than nothing :d
Open issue as of 15/03/21, for reference
import 'dart:math';
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SafeArea(
bottom: false,
child: Scaffold(
// !!! Important part > to disable default scaffold insets
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: Text("Appbar Title"),
),
body: Stack(
children: [
Scrollbar(
child: ListView.builder(
padding: EdgeInsets.all(0.0),
itemCount: 30,
itemBuilder: (context, i) {
return Container(
height: 100,
width: double.infinity,
color: Colors
.primaries[Random().nextInt(Colors.primaries.length)],
);
},
),
),
Align(
alignment: Alignment.bottomLeft,
child: AnimatedPadding(
padding: MediaQuery.of(context).viewInsets,
// You can change the duration and curve as per your requirement:
duration: const Duration(milliseconds: 200),
curve: Curves.decelerate,
child: InputField()),
)
],
)),
);
}
}
class InputField extends StatefulWidget {
InputField({Key key}) : super(key: key);
#override
_InputFieldState createState() => _InputFieldState();
}
class _InputFieldState extends State<InputField> {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.grey[100],
padding: const EdgeInsets.symmetric(vertical: 6),
child: Row(
children: [
SizedBox(
width: 60,
child: Icon(Icons.add_a_photo),
),
Flexible(
child: TextField(
style: Theme.of(context).textTheme.bodyText1,
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Enter text...',
),
),
),
SizedBox(
width: 60,
child: Icon(Icons.send),
),
],
),
);
}
}
Output ->
You can use this package keyboard_visibility and listen to keyboard visibility. Then you can give your logic to implement your feature like you can shorten the home container height. That's not perfect . But I think it's the only way right now.
You need to use keyboard_visibility package and use it to trigger your AnimatedContainer or AnimatedPadding
bool _isKeyboardActive = false;
#override
void initState() {
super.initState();
//add keyboard visibility Listener
KeyboardVisibility.onChange.listen((event) {
setState(() {
_isKeyboardActive = event;
});
});
}
Widget build(BuildContext context){
return AnimatedContainer(
width: _isKeyboardActive ? 200 : MediaQuery.of(context).size.width,
height: 60,
color: Colors.red,
duration: Duration(milliseconds: 600)
)
}
use this as a basis.
You should try setting resizeToAvoidBottomPadding: false like so:
return Scaffold(
key: _scaffoldKey,
resizeToAvoidBottomPadding: false,

Flutter background floating window feature

Is there any way to create a background floating window using Flutter like IMO does.
Background Floating Window: This is a window which can be dragged using fingers and it is not only limited to my app. User can have my app window showing up on different apps too. Some apps that uses it include TrueCaller, IMO, etc.
Here is the screenshot, the boy window can be dragged and when you tap home button, the app will get minimised but this boy window will still be there on the home launcher and if user navigates to some other app, this window will still persist.
Screenshot Example
the below code gives you the result you want
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Unit Converter',
home: Scaffold(
body: SafeArea(
child: Center(
child: Stack(
children: <Widget>[
Container(
decoration: BoxDecoration(
color: Colors.red
),
),
Container(
margin: EdgeInsets.all(20),
width: 150,
height: 200,
decoration: BoxDecoration(
color: Colors.blue
)
)
],
),
),
),
),
);
}
}
A minimal E.g of What you Want:
import 'package:flutter/material.dart';
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: App(),
),
);
}
}
class App extends StatefulWidget {
#override
AppState createState() => AppState();
}
class AppState extends State<App> {
Color caughtColor = Colors.grey;
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Container(
decoration: BoxDecoration(color: Colors.red),
),
DragBox(Offset(0.0, 0.0), 'Box One', Colors.blueAccent),
DragBox(Offset(200.0, 0.0), 'Box Two', Colors.orange),
DragBox(Offset(300.0, 0.0), 'Box Three', Colors.lightGreen),
],
);
}
}
class DragBox extends StatefulWidget {
final Offset initPos;
final String label;
final Color itemColor;
DragBox(this.initPos, this.label, this.itemColor);
#override
DragBoxState createState() => DragBoxState();
}
class DragBoxState extends State<DragBox> {
Offset position = Offset(0.0, 0.0);
#override
void initState() {
super.initState();
position = widget.initPos;
}
#override
Widget build(BuildContext context) {
return Positioned(
left: position.dx,
top: position.dy,
child: Draggable(
data: widget.itemColor,
child: Container(
width: 100.0,
height: 100.0,
color: widget.itemColor,
child: Center(
child: Text(
widget.label,
style: TextStyle(
color: Colors.white,
decoration: TextDecoration.none,
fontSize: 20.0,
),
),
),
),
onDraggableCanceled: (velocity, offset) {
setState(() {
position = offset;
});
},
feedback: Container(
width: 120.0,
height: 120.0,
color: widget.itemColor.withOpacity(0.5),
child: Center(
child: Text(
widget.label,
style: TextStyle(
color: Colors.white,
decoration: TextDecoration.none,
fontSize: 18.0,
),
),
),
),
));
}
}
A simple way to do this would be a stack.
https://docs.flutter.io/flutter/widgets/Stack-class.html

Scrollable flutter popup menu

I'm trying to use flutter popup menu button, but I can't seem to make it smaller with a scroll.
Is it doable? Or am I using the wrong widget to do it?
Image below as reference, would like to show only the first 4 / 5 items, and scroll to show the rest!
Thanks in advance!
You can create your own PopUp Widget instead.
A Card wrapped into a AnimatedContainer with specific dimensions and a ListView inside.
Place this widget on your screen using Stack and Positioned widgets so it will be above other elements on the top | right.
class CustomPopup extends StatefulWidget {
CustomPopup({
#required this.show,
#required this.items,
#required this.builderFunction,
});
final bool show;
final List<dynamic> items;
final Function(BuildContext context, dynamic item) builderFunction;
#override
_CustomPopupState createState() => _CustomPopupState();
}
class _CustomPopupState extends State<CustomPopup> {
#override
Widget build(BuildContext context) {
return Offstage(
offstage: !widget.show,
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
height: widget.show ? MediaQuery.of(context).size.height / 3 : 0,
width: MediaQuery.of(context).size.width / 3,
child: Card(
elevation: 3,
child: MediaQuery.removePadding(
context: context,
removeTop: true,
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: widget.items.length,
itemBuilder: (context, index) {
Widget item = widget.builderFunction(
context,
widget.items[index],
);
return item;
},
),
),
),
),
);
}
}
return Stack(
children: <Widget>[
Container(
color: Colors.blueAccent,
),
Positioned(
right: 0,
top: 60,
child: CustomPopup(
show: shouldShow,
items: [1, 2, 3, 4, 5, 6, 7, 8],
builderFunction: (context, item) {
return ListTile(
title: Text(item.toString()),
onTap: () {}
);
},
),
),
],
);
You can create this in two ways: the first one is PopupMenuButton widget and the second one is PopupRoute.
class HomePage extends StatefulWidget {
#override
_HomepageState createState() => _HomepageState();
}
class _HomepageState extends State {
Listitems = [1,2,3,4,5,6,7,8,9,10,11,12,13];
#override
Widget build(BuildContext context) {
return Scaffold(body: Center(
child: PopupMenuButton(
child: Icon(Icons.add_shopping_cart),
offset: Offset(-1.0, -220.0),
elevation: 0,
color: Colors.transparent,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))),
itemBuilder: (context) {
return <PopupMenuEntry<Widget>>[
PopupMenuItem<Widget>(
child: Container(
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10))),
child: Scrollbar(
child: ListView.builder(
padding: EdgeInsets.only(top: 20),
itemCount: items.length,
itemBuilder: (context, index) {
final trans = items[index];
return ListTile(
title: Text(
trans.toString(),
style: TextStyle(
fontSize: 16,
),
),
onTap: () {
//what would you like to do?
},
);
},
),
),
height: 250,
width: 500,
),
)
];
}),
)
You can also adjust the number of items you want to show by reducing or increasing height of the container. I also added a scrollbar just in case.
You can use maxHeight for constrains property.
...
PopupMenuButton(
constraints:
BoxConstraints(minWidth: context.maxWidth, maxHeight: 300),
...

Resources