From bb2cb9683b011adca411e5c805fbb87fc0867e14 Mon Sep 17 00:00:00 2001 From: ryo Date: Mon, 6 Jan 2025 17:17:26 +0000 Subject: Initial commit --- README | 13 ++++ main.dart | 220 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pubspec.yaml | 90 ++++++++++++++++++++++++ server.dart | 44 ++++++++++++ 4 files changed, 367 insertions(+) create mode 100644 README create mode 100644 main.dart create mode 100644 pubspec.yaml create mode 100644 server.dart diff --git a/README b/README new file mode 100644 index 0000000..cf06593 --- /dev/null +++ b/README @@ -0,0 +1,13 @@ +Flutter Todo App +================ +Copy main.dart to a ready project and compile it. +Run server.dart anywhere you want, it creates users files inside +the folder it is running. + +App Tutorial +============ +It's not user-friendly, I'll work on it later. + +1. Login +2. Insert your todo and press that plus button +3. Save diff --git a/main.dart b/main.dart new file mode 100644 index 0000000..313f649 --- /dev/null +++ b/main.dart @@ -0,0 +1,220 @@ +import 'package:flutter/material.dart'; +import 'dart:io'; +import 'package:http/http.dart' as http; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + brightness: Brightness.dark, + primaryColor: Colors.white, + ), + home: const MyHomePage(title: 'Flutter Demo Home Page'), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key, required this.title}); + + final String title; + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + final TextEditingController _todoController = TextEditingController(); + final TextEditingController _usernameController = TextEditingController(); + final List _values = ['first todo', 'second work']; + final List _dones = [true, false]; + var _username = "test"; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Text App"), + backgroundColor: Color(0xff2d3f76), + ), + body: Center( + child: Container( + padding: EdgeInsets.all(16.0), + child: Column(children: _showTodos()))), + floatingActionButton: FloatingActionButton( + onPressed: () { + setState(() { + if (_todoController.text.isNotEmpty) { + _values.add(_todoController.text); + _dones.add(false); + _todoController.clear(); + } + }); + }, + tooltip: 'Add Todo', + child: const Icon(Icons.add), + ), + bottomNavigationBar: BottomNavigationBar( + items: [ + BottomNavigationBarItem( + icon: IconButton( + onPressed: () => _showDialog(), icon: Icon(Icons.login)), + label: "Login"), + BottomNavigationBarItem( + icon: IconButton( + onPressed: () => _saveTodos(), icon: Icon(Icons.save)), + label: "Save"), + ], + backgroundColor: Color(0xff222436), + )); + } + + List _showTodos() { + List todos = []; + for (int i = 0; i < _values.length; i++) { + String context = _values[i]; + todos.add(Row(children: [ + Checkbox( + value: _dones[i], + onChanged: (bool? value) { + setState(() { + _dones[i] = value!; + }); + }), + SizedBox(width: 10), + Text(context), + Spacer(), + IconButton( + onPressed: () { + setState(() { + int i = _values.indexOf(context); + _values.remove(context); + _dones.removeAt(i); + }); + }, + icon: Icon( + Icons.delete, + color: Colors.red, + ), + ), + ])); + } + if (_values.isEmpty) { + todos.add(Text('Nothing here')); + } + todos.add(SizedBox(height: 10)); + todos.add(TextField( + controller: _todoController, + decoration: const InputDecoration( + border: OutlineInputBorder(), + labelText: 'Insert Todo', + ), + )); + return todos; + } + + Future _showDialog() async { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Login'), + content: TextField( + controller: _usernameController, + decoration: const InputDecoration(hintText: 'Username'), + autofocus: true, + ), + actions: [ + OutlinedButton( + style: OutlinedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text('Cancel'), + ), + ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + onPressed: () { + Navigator.of(context).pop(); + _username = _usernameController.text; + _values.clear(); + _dones.clear(); + _loadTodos(); + _usernameController.clear(); + }, + child: const Text('Login'), + ), + ], + ); + }); + } + + void _loadTodos() async { + String uri = "http://127.0.0.1:6969/$_username.txt"; + try { + http.Response response = await http.get(Uri.parse(uri)); + if (response.statusCode == 200) { + print("Successfully got the data"); + } else { + print('Error getting data: ${response.statusCode}'); + return; + } + + setState(() { + todoDecode(response.body, _values, _dones); + }); + print(_values); + print(_dones); + } on SocketException catch (e) { + print(e.message); + } + } + + void _saveTodos() async { + String uri = "http://127.0.0.1:6969/$_username.txt"; + var data = todoEncode(_values, _dones); + print(data); + try { + http.Response response = await http.post(Uri.parse(uri), body: data); + if (response.statusCode == 200) { + print('Data sent successfully'); + } else { + print('Error sending data: ${response.statusCode}'); + } + } catch (e) { + print(e.toString()); + } + } +} + +String todoEncode(List values, List dones) { + String data = ""; + for (int i = 0; i < values.length; i++) { + data += '${dones[i] ? 1 : 0}:${values[i]}\n'; + } + return data; +} + +void todoDecode(String todos, List values, List dones) { + var tmp = todos.split('\n'); + for (int i = 0; i < tmp.length - 1; i++) { + values.add(tmp[i].substring(2)); + dones.add(tmp[i][0] == '0' ? false : true); + } +} diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..748e57e --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,90 @@ +name: test +description: "Simple todo app." +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: ^3.6.0 + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + #cupertino_icons: ^1.0.8 + http: ^1.2.2 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^5.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/to/asset-from-package + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/to/font-from-package diff --git a/server.dart b/server.dart new file mode 100644 index 0000000..eb5307d --- /dev/null +++ b/server.dart @@ -0,0 +1,44 @@ +import 'dart:convert'; +import 'dart:io'; + +void main() async { + const int port = 6969; + final requests = await HttpServer.bind('localhost', port); + print('Started server bind to localhost:$port'); + await for (final request in requests) { + print('uri: ${request.uri}'); + if (request.method == 'GET') { + print('Processing GET method'); + processGet(request); + } else if (request.method == 'POST') { + print('Processing POST method'); + processPost(request); + } + } +} + +void processGet(HttpRequest request) async { + var filename = request.uri.toString().substring(1); + bool exist = await File(filename).exists(); + String data = ''; + if (exist) { + var data = await File(filename).readAsString(); + print('Sent data:'); + print(data); + } else { + File(filename).writeAsString(''); + print('Created file $filename'); + } + request.response + ..statusCode = HttpStatus.ok + ..write(data) + ..close(); +} + +void processPost(HttpRequest request) async { + var filename = request.uri.path.substring(1); + String data = await utf8.decoder.bind(request).join(); + File(filename).writeAsString(data); + print('Received data:'); + print(data); +} -- cgit v1.2.3