Skip to content

Commit b909ad2

Browse files
Make FortuneBar indicators dynamically size to match underlying item width
1 parent 40e2408 commit b909ad2

1 file changed

Lines changed: 97 additions & 75 deletions

File tree

lib/src/bar/fortune_bar.dart

Lines changed: 97 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,34 @@ class _FortuneBarState extends State<FortuneBar> with SingleTickerProviderStateM
145145
// animateFirst is only for initState
146146
}
147147

148+
double _getIndicatorWidth(
149+
Alignment alignment,
150+
double scrollOffset,
151+
List<double> itemWidths,
152+
double screenWidth,
153+
double totalWidth,
154+
) {
155+
final centerOffset = screenWidth / 2;
156+
final P = scrollOffset % totalWidth;
157+
final relativeP = P < 0 ? P + totalWidth : P;
158+
159+
final screenX = (alignment.x + 1) / 2 * screenWidth;
160+
final distFromCenter = screenX - centerOffset;
161+
162+
var stripPos = relativeP + distFromCenter;
163+
stripPos %= totalWidth;
164+
if (stripPos < 0) stripPos += totalWidth;
165+
166+
double currentPos = 0;
167+
for (final w in itemWidths) {
168+
if (stripPos < currentPos + w) {
169+
return w;
170+
}
171+
currentPos += w;
172+
}
173+
return itemWidths.isEmpty ? 0 : itemWidths.last;
174+
}
175+
148176
@override
149177
Widget build(BuildContext context) {
150178
final theme = Theme.of(context);
@@ -171,82 +199,76 @@ class _FortuneBarState extends State<FortuneBar> with SingleTickerProviderStateM
171199
final itemWidths = widget.items.map((e) => e.weight * unitWidth).toList();
172200
final totalWidth = totalWeight * unitWidth;
173201

174-
return Stack(
175-
children: [
176-
AnimatedBuilder(
177-
animation: _animationManager.animation,
178-
builder: (context, _) {
179-
// Calculate Target
180-
final selectedIndex =
181-
_animationManager.selectedIndex.value;
182-
double targetCenterWeight = 0;
183-
for (int i = 0; i < selectedIndex; i++) {
184-
targetCenterWeight += widget.items[i].weight;
185-
}
186-
targetCenterWeight +=
187-
widget.items[selectedIndex].weight / 2;
188-
189-
final targetTotalScrollWeight =
190-
widget.rotationCount * totalWeight +
191-
targetCenterWeight;
192-
193-
// Pan logic
194-
// We want panning width/2 to correspond to 1 item (avg weight).
195-
// panWeight = -dist * (2 * avgWeight / size.width)
196-
final panWeight = -panState.distance *
197-
(2 * avgWeight / size.width);
198-
199-
final isAnimating =
200-
_animationManager.controller.isAnimating;
201-
final isAnimatingPanFactor = isAnimating ? 0 : 1;
202-
203-
// Current Scroll Weight
204-
final currentScrollWeight =
205-
_animationManager.animation.value *
206-
targetTotalScrollWeight +
207-
panWeight * isAnimatingPanFactor;
208-
209-
final scrollOffset = currentScrollWeight * unitWidth;
210-
211-
return _InfiniteBar(
212-
size: size,
213-
scrollOffset: scrollOffset,
214-
itemWidths: itemWidths,
215-
totalWidth: totalWidth,
216-
children: [
217-
for (int i = 0; i < widget.items.length; i++)
218-
_FortuneBarItem(
219-
item: widget.items[i],
220-
style: widget.items[i].style ??
221-
widget.styleStrategy.getItemStyle(
222-
theme,
223-
i,
224-
widget.items.length,
225-
),
226-
)
227-
],
228-
);
229-
}),
230-
for (var it in widget.indicators)
231-
IgnorePointer(
232-
child: Align(
233-
alignment: it.alignment,
234-
child: SizedBox(
235-
width: size.width / widget.visibleItemCount, // Indicator size assumes uniform?
236-
// The user can customize indicator.
237-
// Standard indicator assumes uniform items.
238-
// Ideally indicator should match the item being pointed at?
239-
// But in FortuneBar, indicator is usually fixed.
240-
// If we have variable weights, the item under indicator has variable width.
241-
// So the indicator width probably shouldn't depend on "visibleItemCount" if items vary?
242-
// Or maybe it should just be a fixed size visual?
243-
// "SizedBox(width: size.width / visibleItemCount)" makes indicator same size as "average item".
244-
height: widget.height,
245-
child: it.child,
246-
),
202+
return AnimatedBuilder(
203+
animation: _animationManager.animation,
204+
builder: (context, _) {
205+
// Calculate Target
206+
final selectedIndex = _animationManager.selectedIndex.value;
207+
double targetCenterWeight = 0;
208+
for (int i = 0; i < selectedIndex; i++) {
209+
targetCenterWeight += widget.items[i].weight;
210+
}
211+
targetCenterWeight += widget.items[selectedIndex].weight / 2;
212+
213+
final targetTotalScrollWeight =
214+
widget.rotationCount * totalWeight + targetCenterWeight;
215+
216+
// Pan logic
217+
// We want panning width/2 to correspond to 1 item (avg weight).
218+
// panWeight = -dist * (2 * avgWeight / size.width)
219+
final panWeight =
220+
-panState.distance * (2 * avgWeight / size.width);
221+
222+
final isAnimating = _animationManager.controller.isAnimating;
223+
final isAnimatingPanFactor = isAnimating ? 0 : 1;
224+
225+
// Current Scroll Weight
226+
final currentScrollWeight = _animationManager.animation.value *
227+
targetTotalScrollWeight +
228+
panWeight * isAnimatingPanFactor;
229+
230+
final scrollOffset = currentScrollWeight * unitWidth;
231+
232+
return Stack(
233+
children: [
234+
_InfiniteBar(
235+
size: size,
236+
scrollOffset: scrollOffset,
237+
itemWidths: itemWidths,
238+
totalWidth: totalWidth,
239+
children: [
240+
for (int i = 0; i < widget.items.length; i++)
241+
_FortuneBarItem(
242+
item: widget.items[i],
243+
style: widget.items[i].style ??
244+
widget.styleStrategy.getItemStyle(
245+
theme,
246+
i,
247+
widget.items.length,
248+
),
249+
)
250+
],
247251
),
248-
),
249-
],
252+
for (var it in widget.indicators)
253+
IgnorePointer(
254+
child: Align(
255+
alignment: it.alignment,
256+
child: SizedBox(
257+
width: _getIndicatorWidth(
258+
it.alignment.resolve(Directionality.of(context)),
259+
scrollOffset,
260+
itemWidths,
261+
size.width,
262+
totalWidth,
263+
),
264+
height: widget.height,
265+
child: it.child,
266+
),
267+
),
268+
),
269+
],
270+
);
271+
},
250272
);
251273
});
252274
});

0 commit comments

Comments
 (0)