bracket_view 1.0.0
bracket_view: ^1.0.0 copied to clipboard
A reusable tournament bracket widget with scroll-driven animations and responsive layout.
bracket_view #
A reusable Flutter tournament bracket widget with scroll-driven animations and responsive layout.
Screenshots #
| Mobile (Scroll + Snap) | Desktop (Full Bracket) |
|---|---|
![]() |
![]() |
Demo #

Features #
- 🏆 Horizontal scroll bracket with snap-to-round behavior
- 🎬 Scroll-driven card position animation (bracket-aligned ↔ compact)
- 🔗 Connector lines between rounds (Type A fixed + Type B animated)
- 📱 Responsive: scroll mode on mobile, full bracket on desktop
- 🎨 Round chip selector with auto-scroll (mobile only)
- 🖌️ Customizable theme (colors, sizes, animation curves)
- 🧩 Custom card builder and team image builder support
- 📦 Generic models — zero external dependencies
- 🌐 Web support with drag-to-scroll
Installation #
dependencies:
bracket_view: ^0.2.0
Usage #
import 'package:bracket_view/bracket_view.dart';
BracketView(
rounds: [
BracketRound(
id: 'qf',
name: 'Quarter Final',
dateLabel: '8-9 Apr | 15-16 Apr',
matches: [
BracketMatch(
id: '1',
teamA: BracketTeam(name: 'PSG', imageUrl: '...'),
teamB: BracketTeam(name: 'Liverpool', imageUrl: '...'),
scoreA: 4,
scoreB: 0,
status: BracketMatchStatus.finished,
winnerSide: BracketWinnerSide.teamA,
label: 'Aggregate',
),
// ...more matches
],
),
// ...more rounds
],
onRoundChanged: (index) => print('Active round: $index'),
onMatchTap: (match) => print('Tapped: ${match.id}'),
teamImageBuilder: (context, url, size) => MyTeamLogo(url: url, size: size),
theme: BracketTheme(
columnWidth: 220,
columnGap: 24,
cardHeight: 90,
),
)
Customization #
Theme #
BracketTheme(
columnWidth: 220.0, // Width of each round column
columnGap: 24.0, // Gap between columns (connector area)
cardHeight: 90.0, // Estimated card height for positioning
compactGap: 12.0, // Gap between cards when snapped
connectorColor: Colors.grey,
connectorWidth: 1.5,
chipSelectedColor: Colors.blue,
chipUnselectedColor: Colors.grey[800],
chipHeight: 32.0,
chipPadding: EdgeInsets.symmetric(horizontal: 10),
snapDuration: Duration(milliseconds: 250),
snapCurve: Curves.easeOutCubic,
previousRoundPeek: 32.0, // How much of the previous round peeks from the left
teamLogoTheme: TeamLogoTheme(
size: 20.0,
fallbackIcon: Icons.sports_soccer,
),
)
Custom Match Card #
BracketView(
rounds: rounds,
matchCardBuilder: (context, match) {
return MyCustomCard(
teamA: match.teamA.name,
teamB: match.teamB.name,
scoreA: match.scoreA,
scoreB: match.scoreB,
);
},
)
Custom Team Image #
BracketView(
rounds: rounds,
teamImageBuilder: (context, imageUrl, size) {
return CachedNetworkImage(
imageUrl: imageUrl ?? '',
width: size,
height: size,
);
},
)
Animation Behavior #
Mobile (Small Screen) #
- Scroll right: Next round cards animate from bracket-aligned (centered between parent pairs) to compact (stacked from top)
- Snap: Columns snap to nearest position on finger release
- Freeze: Snapped columns stay compact — no re-animation on continued forward scroll
- Scroll left: Frozen columns unfreeze and animate back to bracket-aligned
Desktop (Large Screen) #
- All rounds visible at once with round name headers
- Cards in bracket-aligned positions (proper tree layout)
- Horizontal scroll available if content exceeds viewport
- No snap behavior
Connectors #
- Type A (right side): Fixed to parent column — stays in place during animation
- Type B (left side): Moves with animated cards — creates natural "disconnect" effect during transitions
Models #
| Model | Description |
|---|---|
BracketRound |
A round with name, matches, and optional date label |
BracketMatch |
A match with two teams, scores, status, winner, and optional legs |
BracketMatchLeg |
One leg of a two-leg tie (label, score, date, venue) |
BracketTeam |
A team with name and optional image URL |
BracketWinnerSide |
Enum: teamA or teamB |
BracketMatchStatus |
Enum: upcoming, live, finished |
BracketTheme |
Visual configuration for the bracket |
TeamLogoTheme |
Logo/avatar styling within the default match card |
Running the Example #
cd example
flutter run -d chrome
License #
MIT

