name: movietube
description: Chorki style OTT mobile app
publish_to: "none"
environment:
sdk: ">=3.1.0 <4.0.0"
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
http: ^1.1.0
video_player: ^2.7.0
shared_preferences: ^2.2.2
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
import 'package:flutter/material.dart';
import 'ui/login_page.dart';
void main() {
runApp(const MovietubeApp());
}
class MovietubeApp extends StatelessWidget {
const MovietubeApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Movietube',
theme: ThemeData(
primaryColor: Colors.red.shade800,
scaffoldBackgroundColor: Colors.black,
colorScheme: ColorScheme.fromSeed(seedColor: Colors.red),
useMaterial3: true,
),
home: const LoginPage(),
);
}
}
import 'package:flutter/material.dart';
import 'home_page.dart';
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@override State
createState() => _LoginPageState();
}
class _LoginPageState extends State {
final TextEditingController email = TextEditingController();
final TextEditingController password = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('MOVIETUBE',
style: TextStyle(color: Colors.white, fontSize: 28, fontWeight: FontWeight.bold)),
const SizedBox(height: 50),
TextField(
controller: email,
style: const TextStyle(color: Colors.white),
decoration: const InputDecoration(
labelText: 'Email', labelStyle: TextStyle(color: Colors.white),
enabledBorder: UnderlineInputBorder(borderSide: BorderSide(color: Colors.white)),
),
),
TextField(
controller: password, obscureText: true,
style: const TextStyle(color: Colors.white),
decoration: const InputDecoration(
labelText: 'Password', labelStyle: TextStyle(color: Colors.white),
enabledBorder: UnderlineInputBorder(borderSide: BorderSide(color: Colors.white)),
),
),
const SizedBox(height: 30),
ElevatedButton(
onPressed: (){
Navigator.pushReplacement(context,MaterialPageRoute(builder:(_)=> const HomePage()));
},
style: ElevatedButton.styleFrom(backgroundColor: Colors.red.shade800),
child: const Text('LOGIN',style: TextStyle(color: Colors.white)),
)
],
),
),
);
}
}
import 'package:flutter/material.dart';
import '../services/api_service.dart';
import 'video_player_page.dart';
class HomePage extends StatefulWidget { const HomePage({super.key}); @override State createState()=>_HomePageState(); }
class _HomePageState extends State {
List videos=[];
@override void initState(){ super.initState(); loadVideos();}
void loadVideos() async{ videos=await ApiService().fetchVideos(); setState((){});}
@override
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(title: const Text('Movietube', style: TextStyle(color: Colors.white)),backgroundColor: Colors.red.shade800),
backgroundColor: Colors.black,
body: ListView.builder(
itemCount: videos.length,
itemBuilder:(c,i){
var v=videos[i];
return ListTile(
leading: Image.network(v['thumbnail'],width:80,fit:BoxFit.cover),
title: Text(v['title'],style:const TextStyle(color:Colors.white)),
onTap:(){Navigator.push(context,MaterialPageRoute(builder:(_)=>VideoPlayerPage(url:v['url'])));}
);
}),
);
}
}
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
class VideoPlayerPage extends StatefulWidget {
final String url;
const VideoPlayerPage({super.key, required this.url});
@override State createState()=>_VideoPlayerPageState();
}
class _VideoPlayerPageState extends State {
late VideoPlayerController controller;
@override void initState(){
super.initState();
controller=VideoPlayerController.networkUrl(Uri.parse(widget.url))
..initialize().then((_){ setState((){}); controller.play();});
}
@override void dispose(){ controller.dispose(); super.dispose();}
@override Widget build(BuildContext context){
return Scaffold(
backgroundColor: Colors.black,
body: Center(
child: controller.value.isInitialized
? AspectRatio(aspectRatio: controller.value.aspectRatio,child: VideoPlayer(controller))
: const CircularProgressIndicator()));
}
}
import 'dart:convert';
import 'package:http/http.dart' as http;
class ApiService {
final String baseUrl="http://10.0.2.2:3000/api/videos"; // emulator
Future fetchVideos() async {
var res = await http.get(Uri.parse(baseUrl));
if(res.statusCode==200){ return json.decode(res.body);}
return [];
}
}
{
"name": "movietube-backend",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"cors": "^2.8.5",
"express": "^4.18.2",
"mongoose": "^7.6.1"
}
}
const express = require("express");
const mongoose = require("mongoose");
const cors = require("cors");
const app = express();
app.use(cors());
app.use(express.json());
mongoose.connect("mongodb://127.0.0.1/movietube");
const videoSchema = new mongoose.Schema({
title: String,
url: String,
thumbnail: String,
isPremium: Boolean
});
const Video = mongoose.model("Video", videoSchema);
// API 👉 /api/videos
app.get("/api/videos", async (req, res) => {
const videos = await Video.find();
res.json(videos);
});
// Admin Add new video API
app.post("/api/videos", async (req, res) => {
const v = new Video(req.body);
await v.save();
res.json({ success: true });
});
app.listen(3000, () => console.log("backend running on http://localhost:3000"));
Comments
Post a Comment