Open showModalBottomSheet from left side flutter - flutter

I am using ShowModalBottomSheet . I want to open it from left side not from bottom in flutter..

To achieve you desire UI requirements , you should create a custom modal sheet.
It is very simple , you just have to make a container and use tween animation to make it visible and it works like a modal sheet,You can change the direction of the container from its start animating and many more.
I will try to add a code for it later.
This package help you to achieve side modal sheet.
https://pub.dev/packages/side_sheet
Or there is one more way, you should visit this link,I hope it will fullfill your requirements.
https://pub.dev/packages/modal_side_sheet
I think the first package can be more helpfull for you because it has some customization options like you can choose the side etc.

in case someone still needs help. You can use a combination of overlay and Tween to create a custom side sheet.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Custom Sidesheet',
theme: ThemeData(useMaterial3: true),
home: const MyHomePage(title: 'Custom Sidesheet'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
OverlayEntry? sideSheetOverlayEntry;
final sideSheetOverlayLayerLink = LayerLink();
bool isSidebarShown = false;
#override
void initState() {
super.initState();
}
void showSideSheet() {
final sideSheetOverlay = Overlay.of(context)!;
sideSheetOverlayEntry = OverlayEntry(
builder: (context) => Positioned(
height: MediaQuery.of(context).size.height,
child: isSidebarShown
? CompositedTransformFollower(
link: sideSheetOverlayLayerLink,
child: TweenAnimationBuilder(
tween: Tween<double>(begin: 150, end: 300),
duration: const Duration(milliseconds: 300),
builder: (BuildContext context, double size, Widget? child) {
return Material(
child: SafeArea(
child: Container(
width: size,
padding: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
InkWell(
onTap: () {
setState(() {
isSidebarShown = false;
showSideSheet();
});
},
onHover: (value) {},
child: const Icon(
Icons.close,
color: Colors.redAccent,
size: 25.0,
),
)
],
),
Column(
children: [
Container(
child: Text(
"Custom Side Sheet",
overflow: TextOverflow.ellipsis,
style: const TextStyle(color: Colors.blueGrey),
),
),
const Divider(
height: 5.0,
color: Colors.black,
),
Container(
child: Row(children: [
Icon(
Icons.dashboard,
size: 25.0,
color: Colors.blueGrey,
),
Text(
"Home",
overflow: TextOverflow.ellipsis,
style: TextStyle(color: Colors.blueGrey, fontSize: 25.0, fontWeight: FontWeight.w300),
)
]),
)
],
)
],
)),
),
);
},
))
: SizedBox.shrink(),
),
);
sideSheetOverlay.insert(sideSheetOverlayEntry!);
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text(widget.title),
leading: GestureDetector(
onTap: () {
setState(() {
isSidebarShown = true;
showSideSheet();
});
},
child: Icon(
Icons.menu_sharp,
size: 30.0,
),
),
),
body: Container(
color: Color.fromRGBO(223, 230, 233, 1.0),
)),
);
}
}
side sheet not active image
side sheet active image

Related

Sending photo to GridView from preview page

When the user agrees to the photo, I want to send the photo back to the homepage where they can access the photo later on.
Currently, I am just opening the camera again on the PhotoPreview page when the user clicks the second button (OutlineButton). Instead, I want this photo to be sent to the homepage.
Here is the relevant portion of the PhotoPreview page
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () => Navigator.pop(
context), // Go back to the camera to take the picture again
child: Icon(Icons.camera_alt),
),
appBar: AppBar(title: Text('Photo Preview')),
body: Column(children: [
Expanded(child: Image.file(File(widget.imagePath))),
const SizedBox(height: 16.0),
OutlineButton(
onPressed: () {
_openGallery();
Navigator.pop(context);
},
child: Text('Okay'),
borderSide: BorderSide(color: Color(0xff33333D)),
),
]),
);
}
}
The Gridview on my home page, which renders the photo in the format I want, is as such
: GridView.count(
shrinkWrap: true,
crossAxisCount: 2,
crossAxisSpacing: 25,
mainAxisSpacing: 25,
childAspectRatio: (80 / 150),
padding: const EdgeInsets.all(2.0),
children:
List.generate(widget.imageArray.length, (index) {
return Container(
decoration: new BoxDecoration(
color: const Color(0xff000000),
borderRadius: BorderRadius.circular(10),
image: new DecorationImage(
image: FileImage(widget.imageArray[index]),
fit: BoxFit.fill,
colorFilter: new ColorFilter.mode(
Colors.black.withOpacity(0.4),
BlendMode.dstATop),
How can I connect the two, for when the user clicks the OutlineButton that it sends the photo on the preview page to the home screen in the format above?
Edit per answer: Here is full Homepage
import 'dart:io';
import 'package:flutter/material.dart';
class Homepage_1 extends StatefulWidget {
final List<File> imageArray;
Homepage_1({Key key, this.imageArray}) : super(key: key);
#override
_Homepage_1State createState() => _Homepage_1State();
}
class _Homepage_1State extends State<Homepage_1> {
var image;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: Colors.white,
body: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
Padding(
padding:
const EdgeInsets.only(top: 100, left: 40, right: 0, bottom: 0),
child:
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(
'App Name',
style: TextStyle(
fontSize: 60,
fontFamily: 'Avenir',
fontWeight: FontWeight.w900,
),
),
Container(
margin:
EdgeInsets.only(top: 0, left: 0, right: 50, bottom: 0),
child: widget.imageArray.isEmpty
? Column(children: [
Text(
'Yikes! You have no photos',
style: TextStyle(
fontSize: 19,
fontFamily: 'Avenir',
fontWeight: FontWeight.w900,
),
),
Text(
'Click the circular button below'
style: TextStyle(
fontSize: 15,
fontFamily: 'Avenir',
fontWeight: FontWeight.w500,
),
),
])
: GridView.count(
shrinkWrap: true,
crossAxisCount: 2,
crossAxisSpacing: 25,
mainAxisSpacing: 25,
childAspectRatio: (80 / 150),
padding: const EdgeInsets.all(2.0),
children:
List.generate(widget.imageArray.length, (index) {
return Container(
decoration: new BoxDecoration(
color: const Color(0xff000000),
borderRadius: BorderRadius.circular(10),
image: new DecorationImage(
image: FileImage(widget.imageArray[index]),
fit: BoxFit.fill,
colorFilter: new ColorFilter.mode(
Colors.black.withOpacity(0.4),
BlendMode.dstATop),
),
),
);
})))
]),
)
]));
}
}
& here is full Photo preview screen:
import 'package:flutter/material.dart';
import 'dart:io';
class PhotoPreviewScreen extends StatefulWidget {
Function setData;
final String imagePath;
PhotoPreviewScreen({Key key, this.setData, this.imagePath}) : super(key: key);
_PhotoPreviewScreenState createState() => _PhotoPreviewScreenState();
}
class _PhotoPreviewScreenState extends State<PhotoPreviewScreen> {
var image;
Future _openGallery() async {
if (widget.setData != null) {
widget.setData(File(image.path));
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () => Navigator.pop(
context), // Go back to the camera to take the picture again
child: Icon(Icons.camera_alt),
),
appBar: AppBar(title: Text('Photo Preview')),
body: Column(children: [
Expanded(child: Image.file(File(widget.imagePath))),
const SizedBox(height: 16.0),
OutlineButton(
onPressed: () async {
await _openGallery();
Navigator.of(context).pop(widget.imagePath);
},
child: Text('Okay'),
borderSide: BorderSide(color: Color(0xff33333D)),
),
]),
);
}
}
You can pass arguments to pop method and received that as a return value of push .
I wrote a minimal sample for you. Hopefully you get the idea, but if you have any questions, please don't hesitate to ask!
class PhotoPreviewPage extends StatefulWidget {
const PhotoPreviewPage({Key? key, #required this.imagePath})
: super(key: key);
#override
_PhotoPreviewPageState createState() => _PhotoPreviewPageState();
final String imagePath;
}
class _PhotoPreviewPageState extends State<PhotoPreviewPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: OutlineButton(
onPressed: () {
Navigator.of(context).pop(widget.imagePath);
},
child: const Text('OK'),
),
),
);
}
}
/// This is a overly simplified version of the CameraPage
/// Basically, you take a photo and pass that to cameraPreview page
class CameraPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FlatButton(
onPressed: () async {
final imagePath = await _takeAPhoto();
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => PhotoPreviewPage(imagePath: imagePath),
),
);
},
child: Text('take a photo'),
),
),
);
}
Future<String> _takeAPhoto() {
// some logic to take a photo and return imagePath
return imagePath;
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
final List<File> imageArray = [];
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: GridView.count(
crossAxisCount: 2,
children: List.generate(
widget.imageArray.length,
(index) => Container(
decoration: BoxDecoration(
image: DecorationImage(
image: FileImage(widget.imageArray[index]),
fit: BoxFit.fill,
),
),
),
),
),
bottomNavigationBar: Row(
children: [
IconButton(
onPressed: () async {
final filePath = await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => PhotoPreviewPage(),
),
);
if (filePath != null) {
setState(() {
widget.imageArray.add(File(filePath));
});
}
},
icon: Icon(Icons.camera),
),
],
),
);
}
}
But depending on the exact page structure of your app, you might need to look into state management solutions like Bloc or Riverpod.

Is there any way to put custom toolbar on the keypad?

I want to put a custom toolbar on the keypad like the image above. Is it possible in flutter? or should I write code on the iOS or Android side?
You can copy paste run full code below
Please see working demo below
You can use package https://pub.dev/packages/keyboard_overlay
Step 1: Use with HandleFocusNodesOverlayMixin
Step 2: Use FocusNodeOverlay for focusNode
Step 3: Use GetFocusNodeOverlay and set _focusNodeOverlay = GetFocusNodeOverlay(
Step 4: TextField use TextField(focusNode: _focusNodeOverlay,
code snippet
class _MyHomePageState extends State<MyHomePage>
with HandleFocusNodesOverlayMixin {
FocusNodeOverlay _focusNodeOverlay;
#override
void initState() {
_focusNodeOverlay = GetFocusNodeOverlay(
child: TopKeyboardUtil(
Container(
color: Colors.white,
height: 45,
width: MediaQueryData.fromWindow(ui.window).size.width,
child: Row(
children: [
GestureDetector(
child: Icon(Icons.save),
onTap: () => print("click"),
),
...
Spacer(),
Container(
width: 60,
child: Center(
child: DoneButtonIos(
backgroundColor: Colors.white,
textColor: Colors.green,
label: 'Post',
onSubmitted: () {
print("submit");
},
platforms: ['android', 'ios'],
),
),
),
],
),
),
),
);
working demo
full code
import 'package:flutter/material.dart';
import 'package:keyboard_overlay/keyboard_overlay.dart';
import 'dart:ui' as ui;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with HandleFocusNodesOverlayMixin {
FocusNodeOverlay _focusNodeOverlay;
#override
void initState() {
_focusNodeOverlay = GetFocusNodeOverlay(
child: TopKeyboardUtil(
Container(
color: Colors.white,
height: 45,
width: MediaQueryData.fromWindow(ui.window).size.width,
child: Row(
children: [
GestureDetector(
child: Icon(Icons.save),
onTap: () => print("click"),
),
GestureDetector(
child: Icon(Icons.computer),
onTap: () => print("click"),
),
GestureDetector(
child: Icon(Icons.home),
onTap: () => print("click"),
),
Spacer(),
Container(
width: 60,
child: Center(
child: DoneButtonIos(
backgroundColor: Colors.white,
textColor: Colors.green,
label: 'Post',
onSubmitted: () {
print("submit");
},
platforms: ['android', 'ios'],
),
),
),
],
),
),
),
);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
focusNode: _focusNodeOverlay,
style: TextStyle(color: Colors.grey),
decoration: InputDecoration(
labelText: 'Type Something',
labelStyle: TextStyle(color: Colors.black),
fillColor: Colors.orange,
hintStyle: TextStyle(
color: Colors.grey,
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 1.0),
),
),
),
],
),
),
);
}
}
Yes there is a way around in the flutter to achieve this.
Create a widget of the toolbar you want to add.
Set it visible on input focus.
For reference I am sharing the code how I achieve that.
class InputDoneView extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
color: Style.lightGrey,
child: Align(
alignment: Alignment.topRight,
child: Padding(
padding: const EdgeInsets.only(top: 1.0, bottom: 1.0),
child: CupertinoButton(
padding: EdgeInsets.only(right: 24.0, top: 2.0, bottom: 2.0),
onPressed: () {
FocusScope.of(context).requestFocus(new FocusNode());
},
child: Text(
"Done",
style: TextStyle(color: Style.primaryColor,fontWeight: FontWeight.normal)
),
),
),
),
);
}
}
To call this in your main view when input field is focused in and out.
showOverlay(BuildContext context) {
if (overlayEntry != null) return;
OverlayState overlayState = Overlay.of(context);
overlayEntry = OverlayEntry(builder: (context) {
return Positioned(
bottom: MediaQuery.of(context).viewInsets.bottom, right: 0.0, left: 0.0, child: InputDoneView());
});
overlayState.insert(overlayEntry);
}
removeOverlay() {
if (overlayEntry != null) {
overlayEntry.remove();
overlayEntry = null;
}
}

How to move Scaffold's body along with bottomSheet on Flutter?

I'm trying to achieve a particular behavior for my Scaffold when showing a BottomSheet. I want the Scaffold's body to move along with the bottom sheet. That is, when the Bottomheet comes out, the body of the Scaffold should go up with it. Like the image at the right. I'm not sure if my approach is the correct one. Maybe there are other better options to make this behavior possible.
The code with which I'm currently working is here:
Scaffold(
backgroundColor: Colors.purple[100],
resizeToAvoidBottomInset: true,
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Container(
height: 900,
child: Builder(
builder: (context) => Container(
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
FocusScope.of(context).requestFocus(_focusNode);
if (bottomSheetIsOpen) {
bottomSheetIsOpen = false;
Navigator.of(context).pop();
}
},
child: Container(
width: double.infinity,
height: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(height: 50),
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
),
width: 300,
child: TextField(
cursorWidth: 3,
cursorColor: Colors.purple,
onTap: () {
bottomSheetIsOpen = true;
showBottomSheet(
clipBehavior: Clip.hardEdge,
context: context,
builder: (context) => Container(
child: Container(
height: 200,
color: Colors.red,
),
),
);
},
controller: _controller,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
style: TextStyle(fontSize: 24),
showCursor: true,
readOnly: _readOnly,
),
),
Container(
height: 300,
width: 300,
color: Colors.yellow,
),
Container(
height: 250,
width: 300,
color: Colors.orange,
),
],
),
),
),
),
),
),
),
);
You could achieve this with a Stack and two AnimatedPositioned widget:
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Bottomsheet Demo',
debugShowCheckedModeBanner: false,
home: MyHomePage(),
);
}
}
class MyHomePage extends HookWidget {
#override
Widget build(BuildContext context) {
final _isOpenBottomSheet = useState(false);
return Scaffold(
appBar: AppBar(title: Text('Bottomsheet Demo')),
body: LayoutWithBottomSheet(
children: List.generate(
10,
(index) => Container(
height: 100,
color: Colors.red.withGreen(index * 25),
child: Center(
child: Text(
index.toString(),
style: TextStyle(fontSize: 24.0),
),
),
),
).toList(),
bottomSheetChild: Container(color: Colors.yellow),
bottomSheetHeight: 400,
animationSpeed: Duration(milliseconds: 300),
animationCurve: Curves.easeInOutQuad,
isOpenBottomSheet: _isOpenBottomSheet.value,
),
floatingActionButton: FloatingActionButton(
onPressed: () {
_isOpenBottomSheet.value = !_isOpenBottomSheet.value;
},
child: Icon(_isOpenBottomSheet.value
? Icons.arrow_downward
: Icons.arrow_upward),
),
);
}
}
class LayoutWithBottomSheet extends HookWidget {
final List<Widget> children;
final Widget bottomSheetChild;
final Duration animationSpeed;
final Curve animationCurve;
final double bottomSheetHeight;
final bool isOpenBottomSheet;
const LayoutWithBottomSheet({
Key key,
this.children,
this.bottomSheetChild,
this.animationSpeed,
this.animationCurve,
this.bottomSheetHeight,
this.isOpenBottomSheet,
}) : super(key: key);
#override
Widget build(BuildContext context) {
final _scrollController = useScrollController();
final childrenBottom = useState<double>();
final bottomSheetBottom = useState<double>();
useEffect(() {
if (isOpenBottomSheet) {
childrenBottom.value = bottomSheetHeight;
bottomSheetBottom.value = 0;
if (_scrollController.hasClients) {
Future.microtask(
() => _scrollController.animateTo(
_scrollController.offset + bottomSheetHeight,
duration: animationSpeed,
curve: animationCurve,
),
);
}
} else {
childrenBottom.value = 0;
bottomSheetBottom.value = -bottomSheetHeight;
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.offset - bottomSheetHeight,
duration: animationSpeed,
curve: animationCurve,
);
}
}
return;
}, [isOpenBottomSheet]);
return Stack(
children: [
AnimatedPositioned(
duration: animationSpeed,
curve: animationCurve,
left: 0,
right: 0,
top: 0,
bottom: childrenBottom.value,
child: ListView(
controller: _scrollController,
children: children,
),
),
AnimatedPositioned(
duration: animationSpeed,
curve: animationCurve,
left: 0,
right: 0,
bottom: bottomSheetBottom.value,
height: bottomSheetHeight,
child: bottomSheetChild,
),
],
);
}
}
Instead of showing a bottom sheet, you can add a new widget to a Column
reserve:true is the key parameter for navigating to bottom
like:
return Scaffold(
body: SingleChildScrollView(
reserve: true,
child: Column(
children: [
YourWidget(),
if (isOpenBottomSheet)
YourBottomSheet()
],
),
),
);
the complete example:
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 Demo Home Page'),
);
}
}
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 isOpenBottomSheet = false;
final _controller = ScrollController();
void _incrementCounter() {
setState(() {
isOpenBottomSheet = !isOpenBottomSheet;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: SingleChildScrollView(
controller: _controller,
reverse: true,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// your widget
Container(
height: MediaQuery.of(context).size.height,
color: Colors.black),
// your bottom sheet
if (isOpenBottomSheet) Container(height: 400, color: Colors.yellow),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
you can use sliding_up_panel with parallax effect:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("SlidingUpPanelExample"),
),
body: SlidingUpPanel(
parallaxEnabled: true,
parallaxOffset: 0.4
panel: Center(
child: Text("This is the sliding Widget"),
),
body: Center(
child: Text("This is the Widget behind the sliding panel"),
),
),
);
}

How to append Slider under the text within button when i press the raised Button?

Immediately after my app start it will show a button.
As soon as this button is pressed I want to build a slider within the same button to control the volume for this sound.
All I need is to make this slider appear and not the mechanics to control the volume.
what i want to acheive is here..
my button code
void main() => runApp(Home());
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "RelaX",
home: Container(
child: Scaffold(
appBar: AppBar(
elevation: 20.0,
backgroundColor: Color(0xFF001E3D),
title: Text(
'Relax',
style: GoogleFonts.raleway(
fontSize: 30.0,
color: Color(0xFF55b9f3),
),
),
centerTitle: true,
),
body: Column(
children: <Widget>[
RaisedButton(
child: Text("View Slider"),
onPressed: () => print("view slider")),
],
),
),
),
);
}
}
You may use the Visibility widget to set the visibility of the slider. Please see the code below. I'm using Container along with Inkwell to achieve the same effect as RaisedButton.
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: MyApp(),
));
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
double _currentSliderValue = 0;
bool _sliderVisible = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Raised Button"),
),
body: Center(
child: ClipRRect(
borderRadius: const BorderRadius.all(
Radius.circular(20),
),
child: Container(
decoration: BoxDecoration(
color: Colors.blue[200],
borderRadius: const BorderRadius.all(
Radius.circular(20),
),
),
child: Material(
elevation: 2,
child: InkWell(
onTap: () {
setState(() {
_sliderVisible = !_sliderVisible;
});
},
child: Container(
width: 125.0,
height: 125.0,
child: Column(
children: [
const SizedBox(
height: 15,
),
Icon(
Icons.nightlight_round,
color: Colors.indigo[900],
size: 48,
),
const SizedBox(
height: 5,
),
Visibility(
visible: _sliderVisible,
child: Slider(
value: _currentSliderValue,
min: 0,
max: 10,
onChanged: (double value) {
setState(() {
_currentSliderValue = value;
});
},
activeColor: Colors.indigo[900],
inactiveColor: Colors.indigo[900],
),
)
],
),
),
),
color: Colors.transparent,
),
),
),
),
);
}
}

How to animate Alert dialog position in Flutter?

By this simple code I can show dialog on bottom of screen like with this screenshot:
But I have three simple issue:
set margin on bottom of dialog such as 20.0 on showing dialog
using controller.reverse() on dismiss dialog
dismiss dialog on click on outside of dialog
Full source code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void showPopup() {
showDialog(
context: context,
builder: (_) => PopUp(),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: showPopup,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class PopUp extends StatefulWidget {
#override
State<StatefulWidget> createState() => PopUpState();
}
class PopUpState extends State<PopUp> with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<double> opacityAnimation;
Tween<double> opacityTween = Tween<double>(begin: 0.0, end: 1.0);
Tween<double> marginTopTween = Tween<double>(begin: 300, end: 280);
Animation<double> marginTopAnimation;
#override
void initState() {
super.initState();
controller = AnimationController(duration: const Duration(milliseconds: 300), vsync: this);
marginTopAnimation = marginTopTween.animate(controller)
..addListener(() {
setState(() {});
});
controller.forward();
}
#override
Widget build(BuildContext context) {
return FadeTransition(
opacity: opacityTween.animate(controller),
child: Material(
color: Colors.transparent,
child: Container(
margin: EdgeInsets.only(
top: marginTopAnimation.value,
left:20.0,
right:20.0,
),
color: Colors.red,
child: Text("Container"),
),
),
);
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
}
Screenshot:
Code:
floatingActionButton: FloatingActionButton(
onPressed: () {
showGeneralDialog(
barrierLabel: "Label",
barrierDismissible: true,
barrierColor: Colors.black.withOpacity(0.5),
transitionDuration: Duration(milliseconds: 700),
context: context,
pageBuilder: (context, anim1, anim2) {
return Align(
alignment: Alignment.bottomCenter,
child: Container(
height: 300,
child: SizedBox.expand(child: FlutterLogo()),
margin: EdgeInsets.only(bottom: 50, left: 12, right: 12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(40),
),
),
);
},
transitionBuilder: (context, anim1, anim2, child) {
return SlideTransition(
position: Tween(begin: Offset(0, 1), end: Offset(0, 0)).animate(anim1),
child: child,
);
},
);
},
)
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(home: CityPage()));
}
class CityPage extends StatelessWidget {
const CityPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: TextButton(
child: const Text('Press me'),
onPressed: () => BottomDialog().showBottomDialog(context),
),
),
],
),
);
}
}
class BottomDialog {
void showBottomDialog(BuildContext context) {
showGeneralDialog(
barrierLabel: "showGeneralDialog",
barrierDismissible: true,
barrierColor: Colors.black.withOpacity(0.6),
transitionDuration: const Duration(milliseconds: 400),
context: context,
pageBuilder: (context, _, __) {
return Align(
alignment: Alignment.bottomCenter,
child: _buildDialogContent(),
);
},
transitionBuilder: (_, animation1, __, child) {
return SlideTransition(
position: Tween(
begin: const Offset(0, 1),
end: const Offset(0, 0),
).animate(animation1),
child: child,
);
},
);
}
Widget _buildDialogContent() {
return IntrinsicHeight(
child: Container(
width: double.maxFinite,
clipBehavior: Clip.antiAlias,
padding: const EdgeInsets.all(16),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
),
child: Material(
child: Column(
children: [
const SizedBox(height: 16),
_buildImage(),
const SizedBox(height: 8),
_buildContinueText(),
const SizedBox(height: 16),
_buildEmapleText(),
const SizedBox(height: 16),
_buildTextField(),
const SizedBox(height: 16),
_buildContinueButton(),
],
),
),
),
);
}
Widget _buildImage() {
const image =
'https://user-images.githubusercontent.com/47568606/134579553-da578a80-b842-4ab9-ab0b-41f945fbc2a7.png';
return SizedBox(
height: 88,
child: Image.network(image, fit: BoxFit.cover),
);
}
Widget _buildContinueText() {
return const Text(
'Continue with account',
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.w500,
),
);
}
Widget _buildEmapleText() {
return const Text(
'example.com',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
);
}
Widget _buildTextField() {
const iconSize = 40.0;
return Container(
height: 60,
padding: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(width: 1, color: Colors.grey.withOpacity(0.4)),
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
child: Row(
children: [
Container(
width: iconSize,
height: iconSize,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.grey[200],
),
child: const Center(
child: Text('Е'),
),
),
const SizedBox(height: 16),
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
'elisa.g.beckett#gmail.com',
style: TextStyle(
fontWeight: FontWeight.w600,
),
),
Text('**********'),
],
)
],
),
);
}
Widget _buildContinueButton() {
return Container(
height: 40,
width: double.maxFinite,
decoration: const BoxDecoration(
color: Color(0xFF3375e0),
borderRadius: BorderRadius.all(Radius.circular(8)),
),
child: RawMaterialButton(
onPressed: () {
Navigator.of(context, rootNavigator: true).pop();
},
child: const Center(
child: Text(
'Continue',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w500,
),
),
),
),
);
}
}
From top to bottom you can use
bool _fromTop = true;
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
showGeneralDialog(
barrierLabel: "Label",
barrierDismissible: true,
barrierColor: Colors.black.withOpacity(0.5),
transitionDuration: Duration(milliseconds: 700),
context: context,
pageBuilder: (context, anim1, anim2) {
return Align(
alignment: _fromTop ? Alignment.topCenter : Alignment.bottomCenter,
child: Container(
height: 300,
child: SizedBox.expand(child: FlutterLogo()),
margin: EdgeInsets.only(top: 50, left: 12, right: 12, bottom: 50),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(40),
),
),
);
},
transitionBuilder: (context, anim1, anim2, child) {
return SlideTransition(
position: Tween(begin: Offset(0, _fromTop ? -1 : 1), end: Offset(0, 0)).animate(anim1),
child: child,
);
},
);
},
),
);
}
Output:
Not sure if I got your question clearly, if this is what you are looking for, replace your PopUp class with mine.
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void showPopup() {
showDialog(
context: context,
builder: (_) => PopUp(),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: showPopup,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class PopUp extends StatefulWidget {
#override
State<StatefulWidget> createState() => PopUpState();
}
class PopUpState extends State<PopUp> with TickerProviderStateMixin {
AnimationController controller;
double _bottom = 0, _fromTop = 300, _screenHeight, _containerHeight = 300;
#override
void initState() {
super.initState();
controller = AnimationController(duration: const Duration(milliseconds: 300), vsync: this)
..addListener(() {
Timer.periodic(Duration(milliseconds: 15), (timer) {
if (_bottom < _screenHeight - _fromTop - _containerHeight) {
_bottom += 1;
setState(() {});
}
});
});
controller.forward();
}
#override
Widget build(BuildContext context) {
_screenHeight = MediaQuery.of(context).size.height;
return SizedBox(
width: double.infinity,
height: double.infinity,
child: Stack(
children: <Widget>[
Positioned(
bottom: _bottom,
left: 0,
right: 0,
child: Container(height: _containerHeight, color: Colors.green),
),
],
),
);
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
}
I think showModalBottomSheet function does this out of the box.