Delivery package
Everything requested is included below with clean, minimal code and straightforward setup.
- One screen only
- Large “Watch Live” button
- Loads stream from backend
- Video player auto-starts
- Simple loading and error handling
- CORS enabled
/streamendpoint- Returns JSON with HLS URL
- No auth, no extra complexity
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:video_player/video_player.dart';
void main() {
runApp(const LiveStreamApp());
}
class LiveStreamApp extends StatelessWidget {
const LiveStreamApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Live Stream',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
scaffoldBackgroundColor: Colors.white,
textTheme: const TextTheme(
headlineMedium: TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
bodyLarge: TextStyle(fontSize: 22),
bodyMedium: TextStyle(fontSize: 18),
),
useMaterial3: true,
),
home: const LiveStreamPage(),
);
}
}
class LiveStreamPage extends StatefulWidget {
const LiveStreamPage({super.key});
@override
State<LiveStreamPage> createState() => _LiveStreamPageState();
}
class _LiveStreamPageState extends State<LiveStreamPage> {
VideoPlayerController? _videoController;
bool _isLoading = false;
String? _errorMessage;
bool _isPlayerVisible = false;
static const String backendUrl = 'http://10.0.2.2:8000/stream';
Future<void> _watchLive() async {
setState(() {
_isLoading = true;
_errorMessage = null;
});
try {
final response = await http.get(Uri.parse(backendUrl));
if (response.statusCode != 200) {
throw Exception('Stream not available');
}
final data = jsonDecode(response.body) as Map<String, dynamic>;
final streamUrl = data['url'] as String?;
if (streamUrl == null || streamUrl.isEmpty) {
throw Exception('Stream not available');
}
await _videoController?.dispose();
final controller = VideoPlayerController.networkUrl(Uri.parse(streamUrl));
await controller.initialize();
await controller.play();
controller.setLooping(true);
setState(() {
_videoController = controller;
_isPlayerVisible = true;
});
} catch (_) {
setState(() {
_errorMessage = 'Stream not available';
_isPlayerVisible = false;
});
} finally {
if (mounted) {
setState(() {
_isLoading = false;
});
}
}
}
@override
void dispose() {
_videoController?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: const Text(
'Live Stream',
style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
),
),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(24),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _isLoading ? null : _watchLive,
style: ElevatedButton.styleFrom(
minimumSize: const Size(double.infinity, 80),
textStyle: const TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
),
child: const Text('Watch Live'),
),
const SizedBox(height: 24),
if (_isLoading)
const Column(
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text(
'Loading stream...',
style: TextStyle(fontSize: 22),
),
],
),
if (_errorMessage != null)
Text(
_errorMessage!,
style: const TextStyle(
fontSize: 22,
color: Colors.red,
fontWeight: FontWeight.w600,
),
textAlign: TextAlign.center,
),
if (_isPlayerVisible && _videoController != null)
Expanded(
child: Padding(
padding: const EdgeInsets.only(top: 24),
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: AspectRatio(
aspectRatio: _videoController!.value.aspectRatio,
child: VideoPlayer(_videoController!),
),
),
),
),
],
),
),
),
),
);
}
}
name: live_stream_app
description: Simple live streaming MVP for elderly users.
publish_to: 'none'
version: 1.0.0+1
environment:
sdk: '>=3.3.0 <4.0.0'
dependencies:
flutter:
sdk: flutter
http: ^1.2.1
video_player: ^2.8.6
flutter:
uses-material-design: true
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/stream")
def get_stream():
return {
"url": "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8"
}
How to run everything
Minimal setup for stable local testing.
1. Create the FastAPI backend
Create a folder named server. Inside it, create a file named server.py and paste the FastAPI code from the backend tab.
2. Install backend packages
Open a terminal in the server folder and run: pip install fastapi uvicorn
3. Start the backend
Run: uvicorn server:app --reload --host 0.0.0.0 --port 8000 Then open http://127.0.0.1:8000/stream in your browser to confirm the JSON response.
4. Create the Flutter app
Run: flutter create live_stream_app Replace lib/main.dart with the provided Flutter code. Replace the dependencies section in pubspec.yaml with the provided minimal dependencies.
5. Install Flutter dependencies
Inside the Flutter project folder, run: flutter pub get
6. Confirm backend URL
For Android emulator, keep backendUrl as http://10.0.2.2:8000/stream. For iPhone simulator, use http://127.0.0.1:8000/stream. For a physical device, replace it with your computer local IP, such as http://192.168.1.25:8000/stream.
7. Run the Flutter app
Start your emulator or connect a device, then run: flutter run
8. Test the experience
Open the app, tap Watch Live, wait for the loading spinner, and the HLS stream should begin playing automatically. If the backend is unreachable or the URL is invalid, the app shows: Stream not available.
1. 1. Create the FastAPI backend Create a folder named server. Inside it, create a file named server.py and paste the FastAPI code from the backend tab. 2. 2. Install backend packages Open a terminal in the server folder and run: pip install fastapi uvicorn 3. 3. Start the backend Run: uvicorn server:app --reload --host 0.0.0.0 --port 8000 Then open http://127.0.0.1:8000/stream in your browser to confirm the JSON response. 4. 4. Create the Flutter app Run: flutter create live_stream_app Replace lib/main.dart with the provided Flutter code. Replace the dependencies section in pubspec.yaml with the provided minimal dependencies. 5. 5. Install Flutter dependencies Inside the Flutter project folder, run: flutter pub get 6. 6. Confirm backend URL For Android emulator, keep backendUrl as http://10.0.2.2:8000/stream. For iPhone simulator, use http://127.0.0.1:8000/stream. For a physical device, replace it with your computer local IP, such as http://192.168.1.25:8000/stream. 7. 7. Run the Flutter app Start your emulator or connect a device, then run: flutter run 8. 8. Test the experience Open the app, tap Watch Live, wait for the loading spinner, and the HLS stream should begin playing automatically. If the backend is unreachable or the URL is invalid, the app shows: Stream not available.