iosflutteryoutube

Flutter Creating a Card with Text and Youtube Video


I am creating a Card with Flutter, where the Card as the Text section and a Youtube Video Section:

app_page_with_video

I have two issues here:

1. When the full-screen button is clicked, I want only the youtube video becomes the full-screen horizontally. However, when the button is clicked, the whole app becomes horizontally full screen:

The App when full screen button is clicked

2. When Scrolling down the App, the app is frozen:

App when scroll down

With the following error on XCode: xcode error

Could anyone tell me what would the issue in my flutter code?

//main.dart

import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'features/drills/presentation/pages/drills_home_page.dart';
import 'core/config/supabase_config.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Supabase.initialize(url: SupabaseConfig.projectUrl, anonKey: SupabaseConfig.anonKey);

  runApp(const TennisDrillsApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Tennis Drills App',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF339933), // Tennis court green
        ),
        useMaterial3: true,
      ),
      home: const DrillsHomePage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

// drills_home_page.dart

import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
// import '../widgets/drill_card.dart';
import '../widgets/drill_card_with_youtube_video.dart';
import '../../data/repositories/supabase_drill_repository.dart';
import '../../domain/models/drill.dart';

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

  @override
  State<DrillsHomePage> createState() => _DrillsHomePageState();
}

class _DrillsHomePageState extends State<DrillsHomePage> {
  String _selectedCategory = 'all';
  late final SupabaseDrillRepository _drillRepository;
  List<Drill> _drills = [];
  bool _isLoading = true;
  String? _error;

  @override
  void initState() {
    super.initState();
    _drillRepository = SupabaseDrillRepository(Supabase.instance.client);
    _loadDrills();
  }

  Future<void> _loadDrills() async {
    try {
      setState(() {
        _isLoading = true;
        _error = null;
      });

      final drills = await _drillRepository.getDrills();
      setState(() {
        _drills = drills;
        _isLoading = false;
      });
    } catch (e) {
      setState(() {
        _error = 'Failed to load drills: ${e.toString()}';
        _isLoading = false;
      });
    }
  }

  List<Drill> get filteredDrills {
    if (_selectedCategory == 'all') {
      return _drills;
    }
    return _drills.where((drill) => drill.category == _selectedCategory).toList();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Tennis Drills'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        actions: [
          // Forehand filter icon
          IconButton(
            icon: Icon(
              Icons.sports_tennis,
              color: _selectedCategory == 'forehand' ? Theme.of(context).colorScheme.primary : null,
            ),
            tooltip: 'Forehand Drills',
            onPressed: () {
              setState(() {
                _selectedCategory = _selectedCategory == 'forehand' ? 'all' : 'forehand';
              });
            },
          ),
          // Backhand filter icon
          IconButton(
            icon: Transform(
              alignment: Alignment.center,
              transform: Matrix4.rotationY(3.14), // Flip the icon horizontally
              child: Icon(
                Icons.sports_tennis,
                color: _selectedCategory == 'backhand' ? Theme.of(context).colorScheme.primary : null,
              ),
            ),
            tooltip: 'Backhand Drills',
            onPressed: () {
              setState(() {
                _selectedCategory = _selectedCategory == 'backhand' ? 'all' : 'backhand';
              });
            },
          ),
          // Refresh button
          IconButton(icon: const Icon(Icons.refresh), tooltip: 'Refresh Drills', onPressed: _loadDrills),
        ],
      ),
      body: _buildBody(),
    );
  }

  Widget _buildBody() {
    if (_isLoading) {
      return const Center(child: CircularProgressIndicator());
    }

    if (_error != null) {
      return Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(_error!, style: const TextStyle(color: Colors.red)),
            const SizedBox(height: 16),
            ElevatedButton(onPressed: _loadDrills, child: const Text('Retry')),
          ],
        ),
      );
    }

    if (filteredDrills.isEmpty) {
      return const Center(child: Text('No drills found'));
    }

    return ListView.builder(
      itemCount: filteredDrills.length,
      itemBuilder: (context, index) {
        final drill = filteredDrills[index];
        // return DrillCard(drill: drill.toJson());
        return DrillCardWithYoutubeVideo(drill: drill.toJson(), videoId: '928wJjWeVyk');
      },
    );
  }
}

// drill_card_with_youtube_video.dart

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

class DrillCardWithYoutubeVideo extends StatefulWidget {
  final Map<String, dynamic> drill;
  final String videoId;

  const DrillCardWithYoutubeVideo({super.key, required this.drill, required this.videoId});

  @override
  State<DrillCardWithYoutubeVideo> createState() => _DrillCardWithYoutubeVideoState();
}

class _DrillCardWithYoutubeVideoState extends State<DrillCardWithYoutubeVideo> {
  late YoutubePlayerController _controller;

  @override
  void initState() {
    super.initState();
    _controller = YoutubePlayerController(
      initialVideoId: widget.videoId,
      flags: const YoutubePlayerFlags(autoPlay: false, mute: false),
    );
  }

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

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.all(8.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // Text Section
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(widget.drill['title'], style: Theme.of(context).textTheme.titleLarge),
                const SizedBox(height: 8.0),
                Text('Category: ${widget.drill['category']}', style: Theme.of(context).textTheme.bodyMedium),
                if (widget.drill['description'] != null) ...[
                  const SizedBox(height: 8.0),
                  Text(widget.drill['description'], style: Theme.of(context).textTheme.bodyMedium),
                ],
              ],
            ),
          ),

          // YouTube Video Section
          AspectRatio(
            aspectRatio: 16 / 9,
            child: YoutubePlayer(
              controller: _controller,
              showVideoProgressIndicator: true,
              progressIndicatorColor: Theme.of(context).colorScheme.primary,
              progressColors: ProgressBarColors(
                playedColor: Theme.of(context).colorScheme.primary,
                handleColor: Theme.of(context).colorScheme.primary,
              ),
            ),
          ),
        ],
      ),
    );
  }
}


Solution

  • For FullScreen Support

    If fullscreen support is required, wrap your player with YoutubePlayerBuilder

    doc: youtube_player_flutter pub dev

    YoutubePlayerBuilder(
        player: YoutubePlayer(
            controller: _controller,
        ),
        builder: (context, player){
            return Column(
                children: [
                    // some widgets
                    player,
                    //some other widgets
                ],
            );
        ),
    ),