diff --git a/task_manager_app/lib/Todo.dart b/task_manager_app/lib/Todo.dart new file mode 100644 index 0000000..c466e13 --- /dev/null +++ b/task_manager_app/lib/Todo.dart @@ -0,0 +1,17 @@ +class Todo { + final int id; + final int userId; + final String title; + final bool completed; + + Todo({required this.id, required this.userId, required this.title, required this.completed}); + + factory Todo.fromJson(Map json) { + return Todo( + id: json['id'], + userId: json['userId'], + title: json['title'], + completed: json['completed'], + ); + } +} \ No newline at end of file diff --git a/task_manager_app/lib/TodoProvider.dart b/task_manager_app/lib/TodoProvider.dart new file mode 100644 index 0000000..53a90c2 --- /dev/null +++ b/task_manager_app/lib/TodoProvider.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'package:task_manager_app/Todo.dart'; +import 'dart:convert'; + + +class TodoProvider with ChangeNotifier { + List _items = []; + bool _isLoading = false; + String? _errorMessage; + + List get items => _items; + bool get isLoading => _isLoading; + String? get errorMessage => _errorMessage; + + Future fetchTodos() async { + _isLoading = true; + _errorMessage = null; + notifyListeners(); + + final stopwatch = Stopwatch()..start(); + + try { + final response = await http.get( + Uri.parse('https://jsonplaceholder.typicode.com/todos?_limit=20'), + ); + + if (response.statusCode == 200) { + List data = json.decode(response.body); + _items = data.map((item) => Todo.fromJson(item)).toList(); + } else { + _errorMessage = "Server Error: ${response.statusCode}"; + } + } catch (e) { + _errorMessage = "Failed to load data. Please check your connection."; + } finally { + _isLoading = false; + stopwatch.stop(); + print("Fetch took: ${stopwatch.elapsedMilliseconds}ms"); // Performance log + notifyListeners(); + } + } +} \ No newline at end of file diff --git a/task_manager_app/lib/main.dart b/task_manager_app/lib/main.dart index 1659163..b4bdfc7 100644 --- a/task_manager_app/lib/main.dart +++ b/task_manager_app/lib/main.dart @@ -1,24 +1,79 @@ import 'package:flutter/material.dart'; -import 'student_registration_screen.dart'; -import 'home_page.dart'; +import 'package:provider/provider.dart'; +import 'package:task_manager_app/TodoProvider.dart'; + void main() { - runApp(const TaskManagerApp()); + runApp( + ChangeNotifierProvider( + create: (context) => TodoProvider()..fetchTodos(), + child: const MyApp(), + ), + ); } -class TaskManagerApp extends StatelessWidget { - const TaskManagerApp({super.key}); +class MyApp extends StatelessWidget { + const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( - title: 'Task Manager', - theme: ThemeData( - colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), - useMaterial3: true, - ), - home: const HomePage(), - // home: const StudentRegistrationScreen(), + home: const TodoListScreen(), ); } } + +class TodoListScreen extends StatelessWidget { + const TodoListScreen({super.key}); + + @override + Widget build(BuildContext context) { + final provider = Provider.of(context); + + return Scaffold( + appBar: AppBar(title: const Text('Group 1 - Todos')), + body: RefreshIndicator( + onRefresh: () => provider.fetchTodos(), + child: _buildBody(provider), + ), + floatingActionButton: FloatingActionButton( + onPressed: () => provider.fetchTodos(), + child: const Icon(Icons.refresh), + ), + ); + } + + Widget _buildBody(TodoProvider provider) { + if (provider.isLoading) { + return const Center(child: CircularProgressIndicator()); + } + + if (provider.errorMessage != null) { + return Center(child: Text(provider.errorMessage!, style: const TextStyle(color: Colors.red))); + } + + if (provider.items.isEmpty) { + return const Center(child: Text("No items")); + } + + return ListView.builder( + itemCount: provider.items.length, + itemBuilder: (context, index) { + final todo = provider.items[index]; + return ListTile( + leading: CircleAvatar(child: Text(todo.id.toString())), + title: Text( + todo.title, + style: TextStyle( + decoration: todo.completed ? TextDecoration.lineThrough : null, + ), + ), + trailing: Icon( + todo.completed ? Icons.check_circle : Icons.radio_button_unchecked, + color: todo.completed ? Colors.green : Colors.grey, + ), + ); + }, + ); + } +} \ No newline at end of file diff --git a/task_manager_app/pubspec.lock b/task_manager_app/pubspec.lock index 9ce301c..097899c 100644 --- a/task_manager_app/pubspec.lock +++ b/task_manager_app/pubspec.lock @@ -75,6 +75,22 @@ packages: description: flutter source: sdk version: "0.0.0" + http: + dependency: "direct main" + description: + name: http + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" + url: "https://pub.dev" + source: hosted + version: "1.6.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" leak_tracker: dependency: transitive description: @@ -131,6 +147,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.17.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" path: dependency: transitive description: @@ -139,6 +163,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + provider: + dependency: "direct main" + description: + name: provider + sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" + url: "https://pub.dev" + source: hosted + version: "6.1.5+1" sky_engine: dependency: transitive description: flutter @@ -192,6 +224,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.10" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" vector_math: dependency: transitive description: @@ -208,6 +248,14 @@ packages: url: "https://pub.dev" source: hosted version: "15.0.2" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" sdks: dart: ">=3.9.0-0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/task_manager_app/pubspec.yaml b/task_manager_app/pubspec.yaml index e03cd44..675ea2f 100644 --- a/task_manager_app/pubspec.yaml +++ b/task_manager_app/pubspec.yaml @@ -9,6 +9,8 @@ environment: dependencies: flutter: sdk: flutter + http: ^1.2.0 + provider: ^6.1.1 cupertino_icons: ^1.0.6 dev_dependencies: @@ -18,3 +20,5 @@ dev_dependencies: flutter: uses-material-design: true + + diff --git a/task_manager_app/test/widget_test.dart b/task_manager_app/test/widget_test.dart index 6ecd20c..9881d27 100644 --- a/task_manager_app/test/widget_test.dart +++ b/task_manager_app/test/widget_test.dart @@ -10,10 +10,11 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/material.dart'; import 'package:task_manager_app/main.dart'; + void main() { testWidgets('App shows Task Manager title and welcome message', (WidgetTester tester) async { - await tester.pumpWidget(const TaskManagerApp()); + await tester.pumpWidget(const MyApp()); // AppBar title from HomePage. expect(find.textContaining('Week 2'), findsOneWidget);