sports_ground 1.3.0 copy "sports_ground: ^1.3.0" to clipboard
sports_ground: ^1.3.0 copied to clipboard

A comprehensive Flutter package for creating beautiful, interactive sports grounds with drag-and-drop players, team management, and realistic field rendering.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:sports_ground/sports_ground.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sports Ground Examples',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
        useMaterial3: true,
      ),
      home: const ExampleHomePage(),
    );
  }
}

class ExampleHomePage extends StatelessWidget {
  const ExampleHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Sports Ground Examples'),
        backgroundColor: Colors.green.shade800,
        foregroundColor: Colors.white,
      ),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          _buildSportCard(
            context,
            'Cricket Ground',
            'Realistic 3D cricket stadium with multiple camera angles',
            Icons.sports_cricket,
            Colors.green,
            () => Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => const CricketExample()),
            ),
          ),
          const SizedBox(height: 16),
          _buildSportCard(
            context,
            'Tennis Court',
            'Professional tennis court with realistic surfaces',
            Icons.sports_tennis,
            Colors.purple,
            () => Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => const TennisExample()),
            ),
          ),
          const SizedBox(height: 16),
          _buildSportCard(
            context,
            'Rugby Ground',
            'Interactive rugby field with player management',
            Icons.sports_rugby,
            Colors.green,
            () => Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => const RugbyExample()),
            ),
          ),
          const SizedBox(height: 16),
          _buildComingSoonCard(
              'Football Ground', Icons.sports_soccer, Colors.blue),
          const SizedBox(height: 16),
          _buildComingSoonCard(
              'Basketball Court', Icons.sports_basketball, Colors.orange),
        ],
      ),
    );
  }

  Widget _buildSportCard(
    BuildContext context,
    String title,
    String description,
    IconData icon,
    Color color,
    VoidCallback onTap,
  ) {
    return Card(
      elevation: 4,
      child: InkWell(
        onTap: onTap,
        borderRadius: BorderRadius.circular(12),
        child: Padding(
          padding: const EdgeInsets.all(16),
          child: Row(
            children: [
              Container(
                padding: const EdgeInsets.all(12),
                decoration: BoxDecoration(
                  color: color.withValues(alpha: 0.1),
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Icon(icon, color: color, size: 32),
              ),
              const SizedBox(width: 16),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      title,
                      style: const TextStyle(
                        fontSize: 18,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(height: 4),
                    Text(
                      description,
                      style: TextStyle(
                        fontSize: 14,
                        color: Colors.grey[600],
                      ),
                    ),
                  ],
                ),
              ),
              Icon(Icons.arrow_forward_ios, color: Colors.grey[400]),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildComingSoonCard(String title, IconData icon, Color color) {
    return Card(
      elevation: 2,
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Row(
          children: [
            Container(
              padding: const EdgeInsets.all(12),
              decoration: BoxDecoration(
                color: Colors.grey.withValues(alpha: 0.1),
                borderRadius: BorderRadius.circular(8),
              ),
              child: Icon(icon, color: Colors.grey, size: 32),
            ),
            const SizedBox(width: 16),
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    title,
                    style: TextStyle(
                      fontSize: 18,
                      fontWeight: FontWeight.bold,
                      color: Colors.grey[600],
                    ),
                  ),
                  const SizedBox(height: 4),
                  Text(
                    'Coming Soon',
                    style: TextStyle(
                      fontSize: 14,
                      color: Colors.grey[500],
                      fontStyle: FontStyle.italic,
                    ),
                  ),
                ],
              ),
            ),
            Container(
              padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
              decoration: BoxDecoration(
                color: Colors.orange.withValues(alpha: 0.1),
                borderRadius: BorderRadius.circular(12),
              ),
              child: Text(
                'Soon',
                style: TextStyle(
                  fontSize: 12,
                  color: Colors.orange[700],
                  fontWeight: FontWeight.w600,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class CricketExample extends StatefulWidget {
  const CricketExample({super.key});

  @override
  State<CricketExample> createState() => _CricketExampleState();
}

class _CricketExampleState extends State<CricketExample> {
  late List<CricketPlayer> homePlayers;
  late List<CricketPlayer> awayPlayers;
  late CricketScore score;
  CricketViewAngle viewAngle = CricketViewAngle.aerial;
  CricketTimeOfDay timeOfDay = CricketTimeOfDay.day;
  WeatherCondition weather = WeatherCondition.sunny;

  @override
  void initState() {
    super.initState();
    _initializePlayers();
    _initializeScore();
  }

  void _initializePlayers() {
    // Batting team (2 batsmen at stumps)
    homePlayers = [
      CricketPlayer(
        id: 'h1',
        name: 'Virat Kohli',
        position: 'Batsman',
        fieldPosition: const Offset(200, 260), // Striker at bowler's end
        isHomeTeam: true,
        stats: {'Runs': 12847, 'Average': 53.62, 'Centuries': 43},
      ),
      CricketPlayer(
        id: 'h2',
        name: 'Rohit Sharma',
        position: 'Batsman',
        fieldPosition: const Offset(190, 335), // Non-striker at keeper's end
        isHomeTeam: true,
        stats: {'Runs': 9825, 'Average': 48.96, 'Centuries': 29},
      ),
    ];

    // Fielding team (11 players) + 2 umpires
    awayPlayers = [
      // Bowler (at bowling end)
      CricketPlayer(
        id: 'a1',
        name: 'James Anderson',
        position: 'Bowler',
        fieldPosition: const Offset(200, 200), // Near bowler's stumps
        isHomeTeam: false,
        stats: {'Wickets': 696, 'Average': 26.45, 'Best': '7/42'},
      ),
      // Wicket Keeper (behind batsman)
      CricketPlayer(
        id: 'a2',
        name: 'Jos Buttler',
        position: 'Wicket Keeper',
        fieldPosition: const Offset(190, 370), // Behind keeper's stumps
        isHomeTeam: false,
        stats: {'Dismissals': 262, 'Catches': 227, 'Stumpings': 35},
      ),
      // Close-in fielders (near batsman)
      CricketPlayer(
        id: 'a3',
        name: 'Joe Root',
        position: 'Slip',
        fieldPosition: const Offset(210, 380), // First slip
        isHomeTeam: false,
        stats: {'Catches': 178, 'Runs': 11662},
      ),
      CricketPlayer(
        id: 'a4',
        name: 'Ben Stokes',
        position: 'Slip',
        fieldPosition: const Offset(230, 385), // Second slip
        isHomeTeam: false,
        stats: {'Catches': 98, 'Runs': 5061},
      ),
      // CricketPlayer(
      //   id: 'a5',
      //   name: 'Jonny Bairstow',
      //   position: 'Short Leg',
      //   fieldPosition: const Offset(145, 360), // Short leg
      //   isHomeTeam: false,
      //   stats: {'Catches': 89, 'Runs': 4594},
      // ),
      // Inner ring fielders (inside expanded 30-yard circle)
      CricketPlayer(
        id: 'a5',
        name: 'Harry Brook',
        position: 'Point',
        fieldPosition: const Offset(270, 330), // Point - moved further out
        isHomeTeam: false,
        stats: {'Catches': 23, 'Runs': 1456},
      ),
      // CricketPlayer(
      //   id: 'a7',
      //   name: 'Moeen Ali',
      //   position: 'Mid On',
      //   fieldPosition: const Offset(140, 285), // Mid on - moved further out
      //   isHomeTeam: false,
      //   stats: {'Catches': 67, 'Runs': 2914},
      // ),
      CricketPlayer(
        id: 'a6',
        name: 'Chris Woakes',
        position: 'Mid Off',
        fieldPosition: const Offset(250, 285), // Mid off - moved further out
        isHomeTeam: false,
        stats: {'Catches': 45, 'Wickets': 156},
      ),
      CricketPlayer(
        id: 'a7',
        name: 'Mark Wood',
        position: 'Square Leg',
        fieldPosition: const Offset(110, 320), // Square leg - moved further out
        isHomeTeam: false,
        stats: {'Catches': 12, 'Wickets': 89},
      ),
      CricketPlayer(
        id: 'a8',
        name: 'Liam Livingstone',
        position: 'Cover',
        fieldPosition: const Offset(260, 240), // Cover - moved further out
        isHomeTeam: false,
        stats: {'Catches': 34, 'Runs': 1234},
      ),
      CricketPlayer(
        id: 'a9',
        name: 'Sam Curran',
        position: 'Fine Leg',
        fieldPosition: const Offset(120, 250), // Fine leg - moved further out
        isHomeTeam: false,
        stats: {'Catches': 28, 'Wickets': 87},
      ),
      // Outfield fielders (outside expanded 30-yard circle)
      CricketPlayer(
        id: 'a10',
        name: 'Jonny Bairstow',
        position: 'Third Man',
        fieldPosition:
            const Offset(330, 420), // Third man boundary - moved further
        isHomeTeam: false,
        stats: {'Catches': 18, 'Wickets': 76},
      ),
      CricketPlayer(
        id: 'a11',
        name: 'Stuart Broad',
        position: 'Long Leg',
        fieldPosition:
            const Offset(50, 420), // Long leg boundary - moved further
        isHomeTeam: false,
        stats: {'Catches': 65, 'Wickets': 604},
      ),
      // Umpires
      CricketPlayer(
        id: 'u1',
        name: 'Billy Bowden',
        position: 'Umpire',
        fieldPosition: const Offset(190, 230), // Behind keeper's stumps
        isHomeTeam: false,
        stats: {'Matches': 150, 'Experience': '15 years'},
      ),
      CricketPlayer(
        id: 'u2',
        name: 'David Shepherd',
        position: 'Umpire',
        fieldPosition: const Offset(130, 320), // Square leg umpire
        isHomeTeam: false,
        stats: {'Matches': 120, 'Experience': '12 years'},
      ),
    ];
  }

  void _initializeScore() {
    score = CricketScore(
      homeRuns: 287,
      homeWickets: 4,
      homeOvers: 45.3,
      awayRuns: 156,
      awayWickets: 8,
      awayOvers: 38.2,
      currentInnings: '2nd Innings',
    );
  }

  @override
  Widget build(BuildContext context) {
    return CricketGround(
      homePlayers: homePlayers,
      awayPlayers: awayPlayers,
      homeTeamName: 'India',
      awayTeamName: 'England',
      homeTeamColor: Colors.blue,
      awayTeamColor: Colors.red,
      score: score,
      viewAngle: viewAngle,
      timeOfDay: timeOfDay,
      weather: weather,
      onPlayerTap: (player) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('Selected ${player.name} - ${player.position}'),
            duration: const Duration(seconds: 1),
          ),
        );
      },
      onViewAngleChange: (angle) {
        setState(() {
          viewAngle = angle;
        });
      },
      onTimeChange: (time) {
        setState(() {
          timeOfDay = time;
        });
      },
    );
  }
}

class TennisExample extends StatefulWidget {
  const TennisExample({super.key});

  @override
  State<TennisExample> createState() => _TennisExampleState();
}

class _TennisExampleState extends State<TennisExample> {
  late List<TennisPlayer> homePlayers;
  late List<TennisPlayer> awayPlayers;
  late TennisScore score;
  TennisCourtSurface courtSurface = TennisCourtSurface.hard;
  TennisMatchType matchType = TennisMatchType.singlesMen;
  TennisViewAngle viewAngle = TennisViewAngle.top;

  @override
  void initState() {
    super.initState();
    _initializePlayers();
    _initializeScore();
  }

  void _initializePlayers() {
    homePlayers = [
      TennisPlayer(
        id: 'h1',
        name: 'Rafael Nadal',
        fieldPosition: const Offset(200, 150),
        isHomeTeam: true,
        stats: {
          'Aces': 12,
          'Winners': 28,
          'Unforced Errors': 15,
          'Break Points': '3/5',
        },
      ),
    ];

    awayPlayers = [
      TennisPlayer(
        id: 'a1',
        name: 'Novak Djokovic',
        fieldPosition: const Offset(200, 450),
        isHomeTeam: false,
        stats: {
          'Aces': 8,
          'Winners': 22,
          'Unforced Errors': 12,
          'Break Points': '2/4',
        },
      ),
    ];
  }

  void _initializeScore() {
    score = TennisScore(
      homeGames: 6,
      awayGames: 4,
      homeSets: 2,
      awaySets: 1,
      homePoints: '30',
      awayPoints: '15',
      currentSet: 4,
      setHistory: [
        [6, 4],
        [4, 6],
        [7, 5],
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return TennisGround(
      homePlayers: homePlayers,
      awayPlayers: awayPlayers,
      homeTeamName: 'Nadal',
      awayTeamName: 'Djokovic',
      homeTeamColor: Colors.orange,
      awayTeamColor: Colors.blue,
      score: score,
      courtSurface: courtSurface,
      matchType: matchType,
      viewAngle: viewAngle,
      onPlayerTap: (player) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('Selected ${player.name}'),
            duration: const Duration(seconds: 1),
          ),
        );
      },
      onSurfaceChange: (surface) {
        setState(() {
          courtSurface = surface;
        });
      },
      onViewAngleChange: (angle) {
        setState(() {
          viewAngle = angle;
        });
      },
    );
  }
}

class RugbyExample extends StatefulWidget {
  const RugbyExample({super.key});

  @override
  State<RugbyExample> createState() => _RugbyExampleState();
}

class _RugbyExampleState extends State<RugbyExample> {
  late List<RugbyPlayer> homeTeam;
  late List<RugbyPlayer> awayTeam;
  int homeScore = 15;
  int awayScore = 12;

  @override
  void initState() {
    super.initState();
    _initializeTeams();
  }

  void _initializeTeams() {
    homeTeam = [
      RugbyPlayer(
        id: 'h1',
        name: 'John Smith',
        position: 'Fullback',
        jerseyNumber: 15,
        fieldPosition: const Offset(200, 80),
        isHomeTeam: true,
        stats: {'Tries': 3, 'Tackles': 12, 'Passes': 45},
      ),
      RugbyPlayer(
        id: 'h2',
        name: 'Mike Johnson',
        position: 'Wing',
        jerseyNumber: 14,
        fieldPosition: const Offset(100, 100),
        isHomeTeam: true,
        stats: {'Tries': 5, 'Tackles': 8, 'Passes': 23},
      ),
      RugbyPlayer(
        id: 'h3',
        name: 'David Wilson',
        position: 'Centre',
        jerseyNumber: 13,
        fieldPosition: const Offset(150, 130),
        isHomeTeam: true,
        stats: {'Tries': 2, 'Tackles': 15, 'Passes': 67},
      ),
      RugbyPlayer(
        id: 'h4',
        name: 'Chris Brown',
        position: 'Centre',
        jerseyNumber: 12,
        fieldPosition: const Offset(250, 130),
        isHomeTeam: true,
        stats: {'Tries': 1, 'Tackles': 18, 'Passes': 52},
      ),
      RugbyPlayer(
        id: 'h5',
        name: 'Tom Davis',
        position: 'Wing',
        jerseyNumber: 11,
        fieldPosition: const Offset(300, 100),
        isHomeTeam: true,
        stats: {'Tries': 4, 'Tackles': 6, 'Passes': 19},
      ),
      RugbyPlayer(
        id: 'h6',
        name: 'Alex Miller',
        position: 'Fly-half',
        jerseyNumber: 10,
        fieldPosition: const Offset(200, 160),
        isHomeTeam: true,
        stats: {'Tries': 2, 'Tackles': 10, 'Passes': 89},
      ),
      RugbyPlayer(
        id: 'h7',
        name: 'Sam Taylor',
        position: 'Scrum-half',
        jerseyNumber: 9,
        fieldPosition: const Offset(200, 190),
        isHomeTeam: true,
        stats: {'Tries': 1, 'Tackles': 14, 'Passes': 156},
      ),
    ];

    awayTeam = [
      RugbyPlayer(
        id: 'a1',
        name: 'James Anderson',
        position: 'Fullback',
        jerseyNumber: 15,
        fieldPosition: const Offset(200, 520),
        isHomeTeam: false,
        stats: {'Tries': 2, 'Tackles': 16, 'Passes': 38},
      ),
      RugbyPlayer(
        id: 'a2',
        name: 'Robert Lee',
        position: 'Wing',
        jerseyNumber: 14,
        fieldPosition: const Offset(100, 500),
        isHomeTeam: false,
        stats: {'Tries': 3, 'Tackles': 9, 'Passes': 21},
      ),
      RugbyPlayer(
        id: 'a3',
        name: 'Paul White',
        position: 'Centre',
        jerseyNumber: 13,
        fieldPosition: const Offset(150, 470),
        isHomeTeam: false,
        stats: {'Tries': 1, 'Tackles': 20, 'Passes': 43},
      ),
      RugbyPlayer(
        id: 'a4',
        name: 'Mark Green',
        position: 'Centre',
        jerseyNumber: 12,
        fieldPosition: const Offset(250, 470),
        isHomeTeam: false,
        stats: {'Tries': 0, 'Tackles': 22, 'Passes': 56},
      ),
      RugbyPlayer(
        id: 'a5',
        name: 'Steve Clark',
        position: 'Wing',
        jerseyNumber: 11,
        fieldPosition: const Offset(300, 500),
        isHomeTeam: false,
        stats: {'Tries': 2, 'Tackles': 7, 'Passes': 18},
      ),
      RugbyPlayer(
        id: 'a6',
        name: 'Kevin Hall',
        position: 'Fly-half',
        jerseyNumber: 10,
        fieldPosition: const Offset(200, 440),
        isHomeTeam: false,
        stats: {'Tries': 1, 'Tackles': 11, 'Passes': 72},
      ),
      RugbyPlayer(
        id: 'a7',
        name: 'Ryan Young',
        position: 'Scrum-half',
        jerseyNumber: 9,
        fieldPosition: const Offset(200, 410),
        isHomeTeam: false,
        stats: {'Tries': 0, 'Tackles': 13, 'Passes': 134},
      ),
    ];
  }

  @override
  Widget build(BuildContext context) {
    return RugbyGround(
      homeTeam: homeTeam,
      awayTeam: awayTeam,
      homeTeamName: 'Lions',
      awayTeamName: 'Eagles',
      homeTeamColor: Colors.blue,
      awayTeamColor: Colors.red,
      homeScore: homeScore,
      awayScore: awayScore,
      onPlayerTap: (player) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('Tapped on ${player.name} (#${player.jerseyNumber})'),
            duration: const Duration(seconds: 1),
          ),
        );
      },
      onPlayerDrag: (player, position) {
        // Handle player position updates
      },
    );
  }
}
4
likes
160
points
20
downloads

Publisher

verified publishermacincode.com

Weekly Downloads

A comprehensive Flutter package for creating beautiful, interactive sports grounds with drag-and-drop players, team management, and realistic field rendering.

Homepage
Repository (GitHub)
View/report issues
Contributing

Documentation

Documentation
API reference

License

MIT (license)

Dependencies

equatable, flutter, intl, meta, provider, vector_math

More

Packages that depend on sports_ground