gpt4 book ai didi

user-interface - 当屏幕在 flutter 中滚动时动画小部件位置(包括 GIF)

转载 作者:IT王子 更新时间:2023-10-29 07:09:32 31 4
gpt4 key购买 nike

我正在尝试为两行小部件制作动画,以将这些小部件作为一个滚动条折叠成 1 行。我正在尝试在 SliverAppBar 中实现此行为。

为了清楚起见,我在此处包含了一个 GIF 以供引用。我想要您在应用栏中看到的行为,但我希望 2 行变成 1 行,而不是 1 行到 2 行。

gif

这是我目前所拥有内容的一个简短片段。我将 2 个 Row 小部件包装成一个 Wrap 小部件,每个小部件包含 3 个 shr​​inkableBox 小部件。我通过连接到 _scrollController.offset 并进行一些计算来动态调整这些框的大小。行确实会动态移动,但它们不会动画,而是突然移动。

  double kExpandedHeight = 300.0;

Widget build(BuildContext context) {
double size = !_scrollController.hasClients || _scrollController.offset == 0 ? 75.0 : 75 - math.min(45.0, (45 / kExpandedHeight * math.min(_scrollController.offset, kExpandedHeight) * 1.5));
return Scaffold(
body: CustomScrollView(
controller: _scrollController,
slivers: <Widget>[
SliverAppBar(
pinned: true,
expandedHeight: kExpandedHeight,

title: new Text(
"Title!",
),
bottom: PreferredSize(child: Wrap(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ShrinkableBox(
onClick: () {
print("tapped");
},
size: size,
),
ShrinkableBox(
onClick: () {
print("tapped");
},
size: size,
),
ShrinkableBox(
onClick: () {
print("tapped");
},
size: size,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,

children: <Widget>[
ShrinkableBox(
onClick: () {
print("tapped");
},
size: size,
),
ShrinkableBox(
onClick: () {
print("tapped");
},
size: size,
),
ShrinkableBox(
onClick: () {
print("tapped");
},
size: size,
),
],
),
],
), preferredSize: new Size.fromHeight(55),),
)
// ...
// ...Other sliver list content here...
// ...

最佳答案

您可以将 Stack 与 Positioned 小部件一起使用,以根据需要定位 ShrinkableBoxes。由于控制动画的是滚动偏移,因此您不需要使用动画小部件或动画 Controller 或类似的东西。这是一个工作示例,它通过对框的初始和最终位置进行线性插值来计算位置(您可以通过将 Curves.linear 更改为其他曲线来获得不同的动画路径):

import 'dart:math' as math;
import 'dart:ui';

import 'package:flutter/material.dart';

void main() {
runApp(MaterialApp(home: Home()));
}

class Home extends StatefulWidget {
@override
State createState() => HomeState();
}

class HomeState extends State<Home> {
static const double kExpandedHeight = 300.0;

static const double kInitialSize = 75.0;

static const double kFinalSize = 30.0;

static const List<Color> kBoxColors = [
Colors.red,
Colors.green,
Colors.yellow,
Colors.purple,
Colors.orange,
Colors.grey,
];

ScrollController _scrollController = new ScrollController();

@override
void initState() {
_scrollController.addListener(() {
setState(() { /* State being set is the Scroll Controller's offset */ });
});
}

@override
void dispose() {
_scrollController.dispose();
}

Widget build(BuildContext context) {
double size = !_scrollController.hasClients || _scrollController.offset == 0
? 75.0
: 75 -
math.min(45.0,
(45 / kExpandedHeight * math.min(_scrollController.offset, kExpandedHeight) * 1.5));

return Scaffold(
body: CustomScrollView(
controller: _scrollController,
slivers: <Widget>[
SliverAppBar(
pinned: true,
expandedHeight: kExpandedHeight,
title: Text("Title!"),
bottom: PreferredSize(
preferredSize: Size.fromHeight(55),
child: buildAppBarBottom(size),
),
),
SliverFixedExtentList(
itemExtent: 50.0,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return ListTile(title: Text('Item $index'));
},
),
),
],
),
);
}

Widget buildAppBarBottom(double size) {
double t = (size - kInitialSize) / (kFinalSize - kInitialSize);

const double initialContainerHeight = 2 * kInitialSize;
const double finalContainerHeight = kFinalSize;

return Container(
height: lerpDouble(initialContainerHeight, finalContainerHeight, t),
child: LayoutBuilder(
builder: (context, constraints) {
List<Widget> stackChildren = [];
for (int i = 0; i < 6; i++) {
Offset offset = getInterpolatedOffset(i, constraints, t);
stackChildren.add(Positioned(
left: offset.dx,
top: offset.dy,
child: buildSizedBox(size, kBoxColors[i]),
));
}

return Stack(children: stackChildren);
},
),
);
}

Offset getInterpolatedOffset(int index, BoxConstraints constraints, double t) {
Curve curve = Curves.linear;
double curveT = curve.transform(t);

Offset a = getOffset(index, constraints, kInitialSize, 3);
Offset b = getOffset(index, constraints, kFinalSize, 6);

return Offset(
lerpDouble(a.dx, b.dx, curveT),
lerpDouble(a.dy, b.dy, curveT),
);
}

Offset getOffset(int index, BoxConstraints constraints, double size, int columns) {
int x = index % columns;
int y = index ~/ columns;
double horizontalMargin = (constraints.maxWidth - size * columns) / 2;

return Offset(horizontalMargin + x * size, y * size);
}

Widget buildSizedBox(double size, Color color) {
return Container(
height: size,
width: size,
color: color,
);
}
}

关于user-interface - 当屏幕在 flutter 中滚动时动画小部件位置(包括 GIF),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55056721/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com