smart_player_kit 1.0.1 copy "smart_player_kit: ^1.0.1" to clipboard
smart_player_kit: ^1.0.1 copied to clipboard

Advanced Flutter media player — HLS, subtitles, reels, mini player, background playback & analytics.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
import 'package:smart_player_kit/smart_player_kit.dart';

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

// ─────────────────────────────────────────────────────────────────────────────
// App root
// ─────────────────────────────────────────────────────────────────────────────

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

  @override
  State<SmartPlayerExampleApp> createState() => _SmartPlayerExampleAppState();
}

class _SmartPlayerExampleAppState extends State<SmartPlayerExampleApp> {
  final _playerCtrl = SmartPlayerController(
    config: SmartPlayerConfig.network(
      'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4',
      autoPlay: true,
    ),
  );

  final _miniCtrl = MiniPlayerController();
  final _navKey = GlobalKey<NavigatorState>();

  @override
  void dispose() {
    _playerCtrl.dispose();
    _miniCtrl.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'SmartPlayerKit Demo',
      theme: ThemeData.dark(useMaterial3: true),
      debugShowCheckedModeBanner: false,
      navigatorKey: _navKey,
      builder: (context, child) {
        return Stack(
          children: [
            child!,

            // ✅ Live video mini player mein dikhega — same VideoPlayerController
            ValueListenableBuilder<SmartPlayerValue>(
              valueListenable: _playerCtrl,
              builder: (context, val, _) {
                return SmartMiniPlayer(
                  miniController: _miniCtrl,
                  playerController: _playerCtrl,
                  onExpand: _onMiniPlayerExpand,
                  videoWidget: _playerCtrl.videoController != null
                      ? VideoPlayer(_playerCtrl.videoController!)
                      : null,
                );
              },
            ),
          ],
        );
      },
      home: HomeScreen(
        miniCtrl: _miniCtrl,
        playerCtrl: _playerCtrl,
      ),
    );
  }

  void _onMiniPlayerExpand() {
    _miniCtrl.expand();
    _navKey.currentState?.push(
      MaterialPageRoute(
        builder: (_) => BasicPlayerScreen(
          miniCtrl: _miniCtrl,
          playerCtrl: _playerCtrl,
          fromMiniPlayer: true,
        ),
      ),
    );
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// URLs
// ─────────────────────────────────────────────────────────────────────────────

class _Urls {
  static const butterfly =
      'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4';
  static const bee =
      'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4';
  static const hls =
      'https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8';
}

// ─────────────────────────────────────────────────────────────────────────────
// HomeScreen
// ─────────────────────────────────────────────────────────────────────────────

class HomeScreen extends StatelessWidget {
  const HomeScreen({
    super.key,
    required this.miniCtrl,
    required this.playerCtrl,
  });

  final MiniPlayerController miniCtrl;
  final SmartPlayerController playerCtrl;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('SmartPlayerKit Demo')),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          _DemoTile(
            title: '🎬 Basic Video Player',
            subtitle: 'One-liner network video + Mini Player',
            onTap: () => Navigator.push(
              context,
              MaterialPageRoute(
                builder: (_) => BasicPlayerScreen(
                  miniCtrl: miniCtrl,
                  playerCtrl: playerCtrl,
                ),
              ),
            ),
          ),
          _DemoTile(
            title: '📺 HLS Stream',
            subtitle: 'HLS .m3u8 stream playback',
            onTap: () => Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const HlsPlayerScreen()),
            ),
          ),
          _DemoTile(
            title: '🔄 Auto Resume',
            subtitle: 'Netflix-style — last position se continue',
            onTap: () => Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const ResumePlayerScreen()),
            ),
          ),
          _DemoTile(
            title: '📱 Reels Player',
            subtitle: 'TikTok/Instagram style vertical feed',
            onTap: () => Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const ReelsScreen()),
            ),
          ),
          _DemoTile(
            title: '🎵 Audio / Podcast',
            subtitle: 'Podcast + music player',
            onTap: () => Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const AudioScreen()),
            ),
          ),
          _DemoTile(
            title: '🎨 Netflix Theme',
            subtitle: 'Custom red Netflix-style controls',
            onTap: () => Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const ThemedPlayerScreen()),
            ),
          ),
        ],
      ),
    );
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// BasicPlayerScreen
// ─────────────────────────────────────────────────────────────────────────────

class BasicPlayerScreen extends StatefulWidget {
  const BasicPlayerScreen({
    super.key,
    required this.miniCtrl,
    required this.playerCtrl,
    this.fromMiniPlayer = false,
  });

  final MiniPlayerController miniCtrl;
  final SmartPlayerController playerCtrl;
  final bool fromMiniPlayer;

  @override
  State<BasicPlayerScreen> createState() => _BasicPlayerScreenState();
}

class _BasicPlayerScreenState extends State<BasicPlayerScreen> {
  @override
  void initState() {
    super.initState();

    if (!widget.fromMiniPlayer) {
      widget.playerCtrl.loadNewVideo(
        SmartPlayerConfig.network(_Urls.butterfly, autoPlay: true),
      );

      // ✅ PostFrameCallback — build complete hone ke baad call hoga
      WidgetsBinding.instance.addPostFrameCallback((_) {
        widget.miniCtrl.openMiniPlayer(
          title: 'Butterfly',
          subtitle: 'Flutter Official Demo Video',
        );
        widget.miniCtrl.expand();
      });
    }
  }

  void _minimize() {
    widget.miniCtrl.minimize();
    Navigator.pop(context);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: SafeArea(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // ── ✅ Minimize bar — SmartPlayer ke UPAR, alag row mein ──
            Container(
              color: Colors.black,
              padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
              child: Row(
                children: [
                  // Minimize button — clearly visible
                  IconButton(
                    style: IconButton.styleFrom(
                      backgroundColor: Colors.white10,
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(8),
                      ),
                    ),
                    icon: const Icon(
                      Icons.keyboard_arrow_down_rounded,
                      color: Colors.white,
                      size: 26,
                    ),
                    tooltip: 'Minimize',
                    onPressed: _minimize,
                  ),
                  const SizedBox(width: 8),
                  const Text(
                    'Butterfly',
                    style: TextStyle(
                      color: Colors.white,
                      fontSize: 15,
                      fontWeight: FontWeight.w600,
                    ),
                  ),
                  const Spacer(),
                  // Extra actions
                  IconButton(
                    icon: const Icon(Icons.more_vert,
                        color: Colors.white70, size: 22),
                    onPressed: () {},
                  ),
                ],
              ),
            ),

            // ── Video player ──────────────────────────────────────────
            SmartPlayer.config(
              SmartPlayerConfig.network(
                _Urls.butterfly,
                autoPlay: true,
              ),
              title: 'Butterfly',
              controller: widget.playerCtrl,
            ),

            // ── Info panel ────────────────────────────────────────────
            const Padding(
              padding: EdgeInsets.fromLTRB(16, 14, 16, 0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    'Butterfly',
                    style: TextStyle(
                      color: Colors.white,
                      fontSize: 18,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  SizedBox(height: 4),
                  Text(
                    'Flutter Official Demo Video',
                    style: TextStyle(color: Colors.white54, fontSize: 13),
                  ),
                  SizedBox(height: 14),
                  Row(
                    children: [
                      Icon(Icons.info_outline,
                          color: Colors.white24, size: 15),
                      SizedBox(width: 8),
                      Expanded(
                        child: Text(
                          'Upar ↑ arrow button dabao — video chalti rahegi '
                              'aur bottom-right mein mini player dikhega.',
                          style: TextStyle(
                              color: Colors.white24, fontSize: 12),
                        ),
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// HlsPlayerScreen
// ─────────────────────────────────────────────────────────────────────────────

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

  @override
  State<HlsPlayerScreen> createState() => _HlsPlayerScreenState();
}

class _HlsPlayerScreenState extends State<HlsPlayerScreen> {
  late final SmartPlayerController _controller = SmartPlayerController(
    config: SmartPlayerConfig.hls(_Urls.hls, autoPlay: true),
  );

  static const _testVtt = '''
WEBVTT

00:00:00.000 --> 00:00:01.500
Ye ek bee hai 🐝

00:00:01.500 --> 00:00:03.000
SmartPlayerKit — subtitle test

00:00:03.000 --> 00:00:04.500
Subtitle working hai! ✅
''';

  @override
  void initState() {
    super.initState();
    _controller.initialize().then((_) {
      _controller.subtitleController.parseAndLoad(
        _testVtt,
        SubtitleFormat.webvtt,
      );
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('HLS + Subtitle')),
      body: Column(
        children: [
          SmartPlayer.config(
            SmartPlayerConfig.hls(_Urls.hls),
            title: 'Subtitle Demo',
            controller: _controller,
          ),
          const SizedBox(height: 16),
          const Padding(
            padding: EdgeInsets.symmetric(horizontal: 16),
            child: Text(
              '💡 Video ke upar subtitle dikh rahi hai.\n'
                  'Video start hone par 0–4 sec mein text show hoga.',
              style: TextStyle(color: Colors.white70),
            ),
          ),
        ],
      ),
    );
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// ResumePlayerScreen
// ─────────────────────────────────────────────────────────────────────────────

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Auto Resume')),
      body: Column(
        children: [
          SmartPlayer.config(
            SmartPlayerConfig.network(
              _Urls.butterfly,
              resumePlayback: true,
              resumeKey: 'demo_butterfly',
            ),
            title: 'Butterfly — Resume Demo',
          ),
          const Padding(
            padding: EdgeInsets.all(16),
            child: Text(
              'Kuch der chalao → back jao → wapas aao.\nWahi se shuru hoga jahan chhodha tha!',
              style: TextStyle(color: Colors.white70),
            ),
          ),
        ],
      ),
    );
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// ReelsScreen
// ─────────────────────────────────────────────────────────────────────────────

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

  static final _reels = [
    const ReelItem(
      videoUrl: _Urls.butterfly,
      authorName: 'flutter_dev',
      description: 'Flutter animations are amazing! 🦋 #flutter #coding',
      likeCount: 12400,
      commentCount: 234,
    ),
    const ReelItem(
      videoUrl: _Urls.bee,
      authorName: 'sunnydev',
      description: 'SmartPlayerKit — reels mode demo 🎬 #smartplayer',
      likeCount: 8900,
      commentCount: 156,
      isLiked: true,
    ),
    const ReelItem(
      videoUrl: _Urls.butterfly,
      authorName: 'media_magic',
      description: 'Vertical scroll player — TikTok style! ⚡',
      likeCount: 45200,
      commentCount: 890,
    ),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SmartReelsPlayer(
        videos: _reels,
        onLike: (index, item) {
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(
              content: Text('❤️ Liked reel ${index + 1}!'),
              duration: const Duration(seconds: 1),
            ),
          );
        },
        onComment: (index, item) {},
        onShare: (index, item) {},
      ),
    );
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// AudioScreen
// ─────────────────────────────────────────────────────────────────────────────

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

  static const _audioUrl =
      'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Podcast Player')),
      body: SmartAudioPlayer(
        audioUrl: _audioUrl,
        title: 'Flutter Development Tips',
        artist: 'SmartCast Podcast',
        style: AudioPlayerStyle.full,
        theme: SmartPlayerTheme(primaryColor: Colors.deepPurple),
      ),
    );
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// ThemedPlayerScreen
// ─────────────────────────────────────────────────────────────────────────────

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

  @override
  State<ThemedPlayerScreen> createState() => _ThemedPlayerScreenState();
}

class _ThemedPlayerScreenState extends State<ThemedPlayerScreen> {
  late final SmartPlayerController _controller = SmartPlayerController(
    config: SmartPlayerConfig.network(
      _Urls.bee,
      controlsStyle: SmartPlayerControlsStyle.netflix,
      theme: SmartPlayerTheme.netflix(),
      autoPlay: true,
    ),
  );

  static const _testVtt = '''
WEBVTT

00:00:00.000 --> 00:00:01.500
Netflix Style Player 🎬

00:00:01.500 --> 00:00:03.000
SmartPlayerKit — Phase 2

00:00:03.000 --> 00:00:04.500
Subtitle icon press karo ⬆️
''';

  @override
  void initState() {
    super.initState();
    _controller.initialize().then((_) {
      _controller.subtitleController.parseAndLoad(
        _testVtt,
        SubtitleFormat.webvtt,
      );
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      appBar: AppBar(
        backgroundColor: Colors.black,
        title: const Text('Netflix Theme'),
      ),
      body: SmartPlayer.config(
        SmartPlayerConfig.network(
          _Urls.bee,
          controlsStyle: SmartPlayerControlsStyle.netflix,
          theme: SmartPlayerTheme.netflix(),
        ),
        title: 'Netflix Style Player',
        controller: _controller,
      ),
    );
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// _DemoTile
// ─────────────────────────────────────────────────────────────────────────────

class _DemoTile extends StatelessWidget {
  final String title;
  final String subtitle;
  final VoidCallback onTap;

  const _DemoTile({
    required this.title,
    required this.subtitle,
    required this.onTap,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.only(bottom: 12),
      child: ListTile(
        title: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
        subtitle: Text(subtitle),
        trailing: const Icon(Icons.chevron_right),
        onTap: onTap,
      ),
    );
  }
}