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