Размер анимации для виджетов - PullRequest
2 голосов
/ 13 апреля 2020

В Flutter вы предполагаете, что у меня есть простое Container, и я хотел бы изменить его размер до, например, на этом простом скриншоте я хочу изменить верхний контейнер в разделе 1, чтобы иметь верхний контейнер в секции 2 и верхний контейнер в секции 1 должны вести себя только 100.0 после размера до

контейнер B в секции 1 и секции 2 находятся на одной оси без изменить положение, когда контейнер A будет изменен до

enter image description here

например, это то, что я хочу иметь

enter image description here

Я не уверен, с какой анимацией у меня может быть эта функция

этот код работает, но это не то, что я хочу иметь.

Я хочу иметь перетаскиваемый нижний лист с изменяющимся радиусом границы, когда нижний лист достигает верхней части экрана, как с образцом экрана вставки видео и виджетом fade0n / out внутри appbar, который находится внутри верхней части нижнего листа, который виден, когда нижний лист прибывает Верх или скрыть, если нижний лист не имеет максимального размера

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();
  }
}

Ответы [ 2 ]

4 голосов
/ 17 апреля 2020

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,
      ),
    );
  }
}
0 голосов
/ 16 апреля 2020

Я думаю, что вы можете попробовать эту библиотеку, slide_sheet

, когда вы определяете состояние расширения по контроллеру, тогда вы анимируете / увеличиваете контейнер A.

...