2

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

enter image description here

for example, this is what I want to have

enter image description here

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(); } } 
4
  • Where are these containers placed? Containers aa in rows wrapped in column or containers ab in column wrapped in row... it is really hard to help without a code!! Commented Apr 13, 2020 at 21:04
  • @delmin containers are placed inside Columns Commented Apr 13, 2020 at 21:05
  • Is the header size fixed or can it be fixed for the sake of simplicity? Commented Apr 16, 2020 at 10:30
  • 1
    @RémiRousselet size of header is fixed, in fact that's AppBar which that visible when bottom sheet arrived top of screen. like with screen video Commented Apr 16, 2020 at 10:37

2 Answers 2

4
+200

enter image description here enter image description here

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, ), ); } } 
Sign up to request clarification or add additional context in comments.

8 Comments

thanks a lot my friend, :) how about border radius? could you update your code like with video screen?
and there is another things, start of resizing of this pink container should be change when arrived under appBar and finished when arrived on top of screen, start of change height to expand when arrived under appbar and collapse when arrived under appbar too, like with video screen
how can i set elastic animation on opening BottomSheet in first time?
Thanks for noticing bug, I'll try to solve it today or tomorrow morning.
i found another bug :) if you change count of listview items from 200 to for example 10, you can see container of this child doesn't have full height
|
0

I think you may try this library, sliding_sheet

when you detect the expand status by controller, then you animate/enlarge the container A.

1 Comment

i see this library, sliding_sheet is not what i want to have

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.