Related
I have the code below, which I shortened as much as possible to make it easier to deal with. I want to scroll down to show the default appBar, not the background. I did some solutions, but it didn't work. Switch between them with a smooth motion.
I want to use the same existing code because I built on it.
I have attached an illustration of the problem
The main code:
import 'package:flutter/material.dart';
import 'home_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
HomePage code:
import 'package:flutter/material.dart';
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
const SliverPersistentHeader(pinned: true, delegate: SliverHeaderDelegateComponent(expandedHeight: 300)),
SliverList(
delegate: SliverChildListDelegate(
[
Container(
height: 1000,
color: Colors.blue.withOpacity(0.5),
child: const Center(child: Text('Body')),
)
],
),
),
],
),
);
}
}
The SliverHeaderDelegateComponent code :
class SliverHeaderDelegateComponent extends SliverPersistentHeaderDelegate {
final double expandedHeight;
const SliverHeaderDelegateComponent({required this.expandedHeight});
#override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
final appBarSize = expandedHeight - shrinkOffset;
final proportion = 2 - (expandedHeight / appBarSize);
final percent = proportion < 0 || proportion > 1 ? 0.0 : proportion;
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) => SizedBox(
height: expandedHeight + expandedHeight / 2,
child: Stack(
clipBehavior: Clip.none,
children: [
Container(
height: 500,
decoration: const BoxDecoration(
color: Colors.black,
image: DecorationImage(
image: NetworkImage(
'https://www.digitalartsonline.co.uk/cmsdata/slideshow/3662115/baby-driver-rory-hi-res.jpg'),
fit: BoxFit.cover,
),
),
),
PositionedDirectional(
start: 0.0,
end: 0.0,
top: appBarSize > 0 ? appBarSize : 0,
bottom: -100,
child: Opacity(
opacity: percent,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 30 * percent),
child: const Card(
elevation: 20.0,
child: Center(
child: Text("Widget"),
),
),
),
),
),
],
),
),
);
}
#override
double get maxExtent => expandedHeight + expandedHeight / 2;
#override
double get minExtent => kToolbarHeight;
#override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
return true;
}
}
Here is the solution using SliverHeaderDelegateComponent as you requested.
In this example, the AppBar is shown when collapsed, but you can uncomment the commented part if you want to show it on expand. (UPDATE: improved fading as requested in the comment section)
class SliverHeaderDelegateComponent extends SliverPersistentHeaderDelegate {
final double expandedHeight;
const SliverHeaderDelegateComponent({required this.expandedHeight});
#override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
final deadline = (expandedHeight + minExtent);
double percent = shrinkOffset > deadline ? 1 : shrinkOffset / deadline;
final appBarSize = expandedHeight - shrinkOffset;
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) => SizedBox(
height: expandedHeight + expandedHeight / 2,
child: Stack(
clipBehavior: Clip.none,
children: [
// shrinkOffset == 0 // if you want to show it on expand
shrinkOffset > expandedHeight + minExtent // show it on collapse
? AppBar(title: Text('App Bar'))
: Container(
height: 500,
decoration: const BoxDecoration(
color: Colors.black,
image: DecorationImage(
image: NetworkImage(
'https://www.digitalartsonline.co.uk/cmsdata/slideshow/3662115/baby-driver-rory-hi-res.jpg'),
fit: BoxFit.cover,
),
),
),
PositionedDirectional(
start: 0.0,
end: 0.0,
top: appBarSize > 0 ? appBarSize : 0,
bottom: -100,
child: Opacity(
opacity: 1 - percent,
// opacity: percent < 0.5 ? 1 : (1 - percent) * 2, // if you want to start fading when reach half way scroll
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 30 * percent),
child: const Card(
elevation: 20.0,
child: Center(
child: Text("Widget"),
),
),
),
),
),
],
),
),
);
}
#override
double get maxExtent => expandedHeight + expandedHeight / 2;
#override
double get minExtent => kToolbarHeight;
#override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
return true;
}
}
I am fairly new to flutter and currently trying to create a NavBar.
It looks like this:
If I click on the icon, the bar moves to the selected one and the content changes.
However, I have to hit the icon perfectly. I would like to have a "box" around it, so I can tap just near it. Basically divide the space into 3.
I tried the following:
Widget build(BuildContext context) {
return Container(
height: 60,
color: Color(0xff282424),
child: Stack(
children: [
Container(
child: Row(
children: items.map((x) => createNavBarItem(x)).toList(),
),
),
AnimatedContainer(
duration: Duration(milliseconds: 200),
alignment: Alignment(active.offset, 0.7),
child: AnimatedContainer(
duration: Duration(milliseconds: 400),
height: 5,
width: 50,
decoration: BoxDecoration(
color: active.color,
borderRadius: BorderRadius.circular(2.5)),
),
),
],
),
);
}
Widget createNavBarItem(MenuItem item) {
double width = MediaQuery.of(context).size.width;
return SizedBox(
width: width / items.length,
height: 55,
child: GestureDetector(
child: Icon(
Icons.access_time,
color: item.color,
size: 30,
),
onTap: () {
setState(() {
active = item;
navBarUpdate(item);
});
},
),
);
}
The items should take 1/3 of the width. It isn't working that way tho. Any idea on how to increase the "tappable" space?
EDIT
Full code:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.\
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.red,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
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> {
var screens = [Text("Button1"), Text("Button2"), Text("Button3")];
int currentScreen = 0;
void changeIndex(int index) => setState(() {
currentScreen = index;
});
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.orange,
child: Stack(
children: [
SafeArea(child: screens[currentScreen]),
Container(
alignment: Alignment.bottomCenter, child: NavBar(changeIndex))
],
),
),
);
}
}
class MenuItem {
final String name;
final Color color;
final double offset;
MenuItem(this.name, this.color, this.offset);
}
class NavBar extends StatefulWidget {
#override
State<StatefulWidget> createState() => NavBarState(navBarUpdate);
late Function(int) navBarUpdate;
NavBar(this.navBarUpdate);
}
class NavBarState extends State<NavBar> {
var items = [
MenuItem("Test", Colors.red, -0.76),
MenuItem("Test2", Colors.green, 0),
MenuItem("Test3", Colors.yellow, 0.76)
];
late MenuItem active;
late Function(MenuItem) navBarUpdate;
#override
void initState() {
super.initState();
active = items[0];
}
NavBarState(Function(int) navBarUpdate) {
this.navBarUpdate = (item) {
navBarUpdate(items.indexOf(item));
};
}
#override
Widget build(BuildContext context) {
return Container(
height: 60,
color: Color(0xff282424),
child: Stack(
children: [
Container(
child: Row(
children: items.map((x) => createNavBarItem(x)).toList(),
),
),
AnimatedContainer(
duration: Duration(milliseconds: 200),
alignment: Alignment(active.offset, 0.7),
child: AnimatedContainer(
duration: Duration(milliseconds: 400),
height: 5,
width: 50,
decoration: BoxDecoration(
color: active.color,
borderRadius: BorderRadius.circular(2.5)),
),
),
],
),
);
}
Widget createNavBarItem(MenuItem item) {
double width = MediaQuery.of(context).size.width;
return SizedBox(
width: width / items.length,
height: 55,
child: GestureDetector(
child: Icon(
Icons.access_time,
color: item.color,
size: 30,
),
onTap: () {
setState(() {
active = item;
navBarUpdate(item);
});
},
),
);
}
}
You can use behavior: HitTestBehavior.translucent, or opaque on createNavBarItem
child: GestureDetector(
behavior: HitTestBehavior.translucent,
You can swap your GestureDetector on top level widget from Icon.
Widget createNavBarItem(MenuItem item) {
double width = MediaQuery.of(context).size.width;
return GestureDetector(
child: Container(
color: Colors.transparent,
width: width / items.length,
height: 55,
child: Icon(
Icons.access_time,
color: item.color,
size: 30,
),
),
onTap: () {
setState(() {
active = item;
navBarUpdate(item);
});
},
);
}
I want to draw multiple rectangles like this
I have used CustomPainter to get desired result but getting something different.
I have shared the code and undesired output image below,
Please help me to draw the overlapped ractangles with some positioning to get desired output.
import 'package:flutter/material.dart';
class SplashScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
double _width = MediaQuery.of(context).size.width;
// double _height = MediaQuery.of(context).size.height;
return Scaffold(
body: Container(
width: _width,
// height: _height,
child: Center(
child: Stack(children: <Widget>[
CustomPaint(
painter: DrawCustomRect(0, 0, 80, 112, 0xFFFA2A2A),
),
CustomPaint(
painter: DrawCustomRect(0, 0, 16, 96, 0xFFFF5454),
),
CustomPaint(
// DrawCustomRect(_left,_top,_right, _bottom, _color);
painter: DrawCustomRect(0, 0, 16, 80, 0xFFFF8D8D),
),
]),
),
),
);
}
}
class DrawCustomRect extends CustomPainter {
final double _left;
final double _top;
final double _right; //width
final double _bottom; //height
final int _color;
DrawCustomRect(this._left, this._top, this._right, this._bottom, this._color);
#override
void paint(Canvas canvas, Size size) {
canvas.drawRect(
new Rect.fromLTRB(this._left, this._top, this._right, _bottom),
new Paint()..color = new Color(_color),
);
}
#override
bool shouldRepaint(DrawCustomRect oldDelegate) {
return false;
}
}
You can achieve this result using Containers inside a Stack:
Center(
child: Stack(
alignment: Alignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 300),
width: 75,
height: 100,
color: Colors.red[300],
),
Container(
margin: EdgeInsets.only(left: 150),
width: 150,
height: 200,
color: Colors.red[600],
),
Container(
width: 225,
height: 300,
color: Colors.red[900],
),
],
),
),
And then adjust the sizes and colors based on what you want.
The result:
There are two approaches to it. The first and easy one is to use a combination of stack and container widget to achieve the above effect. Here's the code I used
import 'package:flutter/material.dart';
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> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Stack(
children: <Widget>[
Align(
alignment: Alignment(0.4, 0.1),
child: Container(
width: 60,
height: 60,
color: Colors.red[200],
),
),
Align(
alignment: Alignment(0.3, 0.1),
child: Container(
width: 70,
height: 70,
color: Colors.red[300],
),
),
Align(
alignment: Alignment(0.2, 0.1),
child: Container(
width: 80,
height: 80,
color: Colors.red[400],
),
),
Align(
alignment: Alignment(0.1, 0.1),
child: Container(
width: 90,
height: 90,
color: Colors.red[500],
),
),
],
),
),
);
}
}
The other one is by using the custom painter. You just have to fix some positioning in your code.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SplashScreen(),
);
}
}
class SplashScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
double _width = MediaQuery.of(context).size.width;
// double _height = MediaQuery.of(context).size.height;
return Scaffold(
body: Container(
width: _width,
// height: _height,
child: Center(
child: Stack(
alignment: Alignment.center,
children: <Widget>[
CustomPaint(
painter: DrawCustomRect(112, 32, 0, 80, 0xFFFF8D8D),
),
CustomPaint(
painter: DrawCustomRect(96, 16, 0, 96, 0xFFFF5454),
),
CustomPaint(
painter: DrawCustomRect(80, 0, 0, 112, 0xFFFA2A2A),
),
],
),
),
),
);
}
}
class DrawCustomRect extends CustomPainter {
final double _left;
final double _top;
final double _right; //width
final double _bottom; //height
final int _color;
DrawCustomRect(this._left, this._top, this._right, this._bottom, this._color);
#override
void paint(Canvas canvas, Size size) {
canvas.drawRect(
new Rect.fromLTRB(this._left, this._top, this._right, _bottom),
new Paint()..color = new Color(_color),
);
}
#override
bool shouldRepaint(DrawCustomRect oldDelegate) {
return false;
}
}
In Flutter you suppose I have a simple Container and I would like to change the size of that to up, for example in this simple screenshot I want to change top container in section 1 to up to have a top container in section 2
and top container in section 1 should behave only 100.0 after size to up
container B in section 1 and section 2 are in the same axis without change position when container A will be resized to up
for example, this is what I want to have
I'm not sure with which one animation I can have this feature
this code work, but this is not what I want to have.
i want to have draggable bottom sheet with changing border radius when bottom sheet arrived to top of screen like with pastes sample video screen and fade0n/out widget inside appbar which that inside top of bottom sheet, which that visible when bottom sheet arrived top or hide when bottom sheet don't have maximum size
import 'package:flutter/material.dart';
void main()=>runApp(SizeUp());
class SizeUp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'test',
home: SizeUpAnim(),
);
}
}
class SizeUpAnim extends StatefulWidget {
#override
State<StatefulWidget> createState() =>_SizeUpAnim();
}
class _SizeUpAnim extends State with SingleTickerProviderStateMixin {
AnimationController _controller;
// ignore: constant_identifier_names
static const _PANEL_HEADER_HEIGHT = 32.0;
bool get _isPanelVisible {
final AnimationStatus status = _controller.status;
return status == AnimationStatus.completed ||
status == AnimationStatus.forward;
}
#override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 100), value: 1.0, vsync: this);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 8.0,
title: const Text("Step4"),
leading: IconButton(
onPressed: () {
_controller.fling(velocity: _isPanelVisible ? -1.0 : 1.0);
},
icon: AnimatedIcon(
icon: AnimatedIcons.close_menu,
progress: _controller.view,
),
),
),
body: Column(
children: <Widget>[
Expanded(
child: LayoutBuilder(
builder: _buildStack,
),
),
Text('aaa'),
],
),
);
}
Animation<RelativeRect> _getPanelAnimation(BoxConstraints constraints) {
final double height = constraints.biggest.height;
final double top = height - _PANEL_HEADER_HEIGHT;
const double bottom = -_PANEL_HEADER_HEIGHT;
return RelativeRectTween(
begin: RelativeRect.fromLTRB(0.0, top, 0.0, bottom),
end: const RelativeRect.fromLTRB(0.0, 0.0, 0.0, 0.0),
).animate( CurvedAnimation(parent: _controller, curve: Curves.linear));
}
Widget _buildStack(BuildContext context, BoxConstraints constraints) {
final Animation<RelativeRect> animation = _getPanelAnimation(constraints);
final ThemeData theme = Theme.of(context);
return Container(
color: theme.primaryColor,
child: Stack(
children: <Widget>[
const Center(
child: Text("base"),
),
PositionedTransition(
rect: animation,
child: Material(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16.0),
topRight: Radius.circular(16.0)),
elevation: 12.0,
child: Container(
height: _PANEL_HEADER_HEIGHT,
child: const Center(child: Text("panel")),
),
),
),
],
),
);
}
#override
void dispose() {
super.dispose();
_controller.dispose();
}
}
import 'package:flutter/material.dart';
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(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool isLong = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('First'),
),
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('hey'),
RaisedButton(
onPressed: () {
setImages();
setState(() {
isLong = !isLong;
});
},
child: Text(isLong ? 'long' : 'short'),
),
RaisedButton(
onPressed: _onPressed,
child: Text('open'),
)
],
),
),
);
}
_onPressed() {
Navigator.of(context)
.push(TransparentRoute(builder: (context) => NewWidget(images)));
}
List<String> images = List.generate(
5,
(_) => 'http://placeimg.com/100/100/any',
);
void setImages() {
images = List.generate(
isLong ? 5 : 25,
(_) => 'http://placeimg.com/100/100/any',
);
}
}
class NewWidget extends StatefulWidget {
const NewWidget(this.images, {Key key}) : super(key: key);
final List<String> images;
#override
_NewWidgetState createState() => _NewWidgetState();
}
class _NewWidgetState extends State<NewWidget> {
bool isBig = false;
bool isStack = false;
bool isBounsing = true;
final double topOffset = 200;
final double miniHandleHeigh = 30;
double safeAreaPadding = 0;
double startBigAnimationOffset;
double startStickyOffset;
double backgroundHeight = 0;
double get savedAppBarHeigh => safeAreaPadding + kToolbarHeight;
final ScrollController controller = ScrollController();
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
super.initState();
}
void _afterLayout(_) {
var media = MediaQuery.of(context);
safeAreaPadding = media.padding.top;
startBigAnimationOffset = topOffset - savedAppBarHeigh;
startStickyOffset = startBigAnimationOffset + 20;
backgroundHeight = media.size.height - miniHandleHeigh - topOffset;
var canScroll = !_isImageSizeBiggerThenBottomSheetSize(
media.size.width,
media.size.height,
);
controller.addListener(
canScroll ? withoutScrolling : withScrolling,
);
}
void withoutScrolling() {
var offset = controller.offset;
if (offset < 0) {
goOut();
} else {
controller.animateTo(
0,
duration: Duration(milliseconds: 100),
curve: Curves.easeIn,
);
}
}
void withScrolling() {
var offset = controller.offset;
if (offset < 0) {
goOut();
} else if (offset < startBigAnimationOffset && isBig) {
setState(() {
isBig = false;
});
} else if (offset > startBigAnimationOffset && !isBig) {
setState(() {
isBig = true;
});
} else if (offset > startStickyOffset && !isStack) {
setState(() {
isStack = true;
});
} else if (offset < startStickyOffset && isStack) {
setState(() {
isStack = false;
});
}
if (offset < topOffset && !isBounsing) {
setState(() => isBounsing = true);
} else if (offset > topOffset && isBounsing) {
setState(() => isBounsing = false);
}
}
void goOut() {
controller.dispose();
Navigator.of(context).pop();
}
_isImageSizeBiggerThenBottomSheetSize(
double screenWidth,
double screenHeight,
) {
// padding: 10, 3 in row;
print(screenHeight);
var itemHeight = (screenWidth - 20) / 3;
print(itemHeight);
var gridMaxHeight = screenHeight - topOffset - miniHandleHeigh;
print(gridMaxHeight);
return (widget.images.length / 3).floor() * itemHeight > gridMaxHeight;
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: isStack ? Colors.white : Colors.transparent,
body: Stack(
children: [
Positioned(
bottom: 0,
right: 0,
left: 0,
child: Container(
constraints: BoxConstraints(minHeight: backgroundHeight),
decoration: BoxDecoration(
color: Colors.white,
),
),
),
ListView(
physics:
isBounsing ? BouncingScrollPhysics() : ClampingScrollPhysics(),
controller: controller,
children: <Widget>[
Container(
alignment: Alignment.bottomCenter,
height: topOffset,
child: TweenAnimationBuilder(
tween: Tween(begin: 0.0, end: isBig ? 1.0 : 0.0),
duration: Duration(milliseconds: 500),
child: Align(
alignment: Alignment.topCenter,
child: Padding(
padding: EdgeInsets.only(top: 15),
child: Container(
decoration: BoxDecoration(
color: Colors.black38,
borderRadius: BorderRadius.circular(2),
),
height: 5,
width: 60,
),
),
),
builder: (_, number, child) {
return Container(
height: savedAppBarHeigh * number + miniHandleHeigh,
decoration: BoxDecoration(
borderRadius: BorderRadius.vertical(
top: Radius.circular((1 - number) * 20)),
color: Colors.white,
),
child: Opacity(opacity: 1 - number, child: child),
);
}),
),
Container(
padding: EdgeInsets.all(10),
constraints: BoxConstraints(
minHeight:
MediaQuery.of(context).size.height - savedAppBarHeigh),
decoration: BoxDecoration(
color: Colors.white,
),
child: getGrid(),
)
],
),
if (isStack)
_AppBar(
title: 'Gallery',
)
],
),
);
}
Widget getGrid() {
return GridView.count(
crossAxisSpacing: 10,
mainAxisSpacing: 10,
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
crossAxisCount: 3,
children: widget.images.map((url) {
return Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.blueAccent,
),
),
child: Image(
image: NetworkImage(url),
),
);
}).toList(),
);
}
}
class _AppBar extends StatelessWidget {
const _AppBar({Key key, this.title}) : super(key: key);
final Color backgroundColor = Colors.white;
final Color color = Colors.grey;
final String title;
#override
Widget build(BuildContext context) {
return Material(
elevation: 5,
child: Container(
height: kToolbarHeight + MediaQuery.of(context).padding.top,
color: backgroundColor,
child: OverflowBox(
alignment: Alignment.topCenter,
maxHeight: 200,
child: SafeArea(
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: kToolbarHeight),
child: AppBar(
centerTitle: false,
backgroundColor: backgroundColor,
iconTheme: IconThemeData(
color: color, //change your color here
),
primary: false,
title: Text(
title,
style: TextStyle(color: color),
),
elevation: 0,
),
),
),
),
),
);
;
}
}
class TransparentRoute extends PageRoute<void> {
TransparentRoute({
#required this.builder,
RouteSettings settings,
}) : assert(builder != null),
super(settings: settings, fullscreenDialog: false);
final WidgetBuilder builder;
#override
bool get opaque => false;
#override
Color get barrierColor => null;
#override
String get barrierLabel => null;
#override
bool get maintainState => true;
#override
Duration get transitionDuration => Duration(milliseconds: 350);
#override
Widget buildPage(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
final result = builder(context);
return Container(
color: Colors.black.withOpacity(0.5),
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 1),
end: Offset.zero,
).animate(CurvedAnimation(
parent: animation,
curve: Curves.easeIn,
)),
child: result,
),
);
}
}
I think you may try this library, sliding_sheet
when you detect the expand status by controller, then you animate/enlarge the container A.
I was looking at the properties for the different dialog classes and didn't see anything that would change it's shape. Is there a way to change a dialogs shape?
You can create a variety of shaped dialogs using Container in combination with existing clipping widgets (Painting and Effect Widgets) or by extending CustomClipper. The below will give you a diamond-shaped dialog. There are existing widgets like ClipOval that work out of the box without any customization (see screenshot below). If you want to try out ClipOval, simply replace ClipPath with ClipOval and comment out clipper:. Check out the painting.dart class to learn about creating custom paths.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Shaped Dialog Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
dialogBackgroundColor: Colors.transparent,
),
home: MyHomePage(title: 'Flutter Shaped Dialog Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
_showShapedDialog();
}),
);
}
_showShapedDialog() {
showDialog(
context: context,
builder: (context) {
return Padding(
padding: const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0),
child: ClipPath(
child: Material(
color: Colors.white,
child: Center(
child: Container(
alignment: FractionalOffset.center,
height: MediaQuery.of(context).size.width / 2.0,
width: MediaQuery.of(context).size.width / 2.0,
decoration: BoxDecoration(
border: Border.all(),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(bottom: 20.0),
child: Text(
'Clipping to a path is expensive. Certain shapes have more optimized widgets.',
textAlign: TextAlign.center,
),
),
FlatButton(
child: Text(
'OK',
style: TextStyle(color: Colors.blue),
),
onPressed: () {
Navigator.pop(context);
},
),
],
),
),
),
),
clipper: _MyClipper(), // Comment this out if you want to replace ClipPath with ClipOval
),
);
},
);
}
}
class _MyClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
final path = Path();
path.lineTo(size.width / 2.0, 0.0);
path.lineTo(0.0, size.height / 2.0);
path.lineTo(size.width / 2.0, size.height);
path.lineTo(size.width, size.height / 2.0);
path.lineTo(size.width / 2.0, 0.0);
path.close();
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}
Like that
Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
child: Text('Dialog'),
);
AlertDialog(
shape: RoundedRectangleBorder(borderRadius:
BorderRadius.all(Radius.circular(15))),
title: Text('Your title!'),
content: Container(),
);