From caa2502a1e7245329c1438054c1196207f55d1d4 Mon Sep 17 00:00:00 2001 From: rl544 Date: Fri, 9 May 2025 16:56:41 +0900 Subject: [PATCH] Add : Web file gethering --- android/build.gradle.kts | 4 - lib/main.dart | 378 ++++++++++++++++++++++++++------------- pubspec.lock | 24 +++ pubspec.yaml | 1 + 4 files changed, 283 insertions(+), 124 deletions(-) diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 052e20d..89176ef 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -19,7 +19,3 @@ subprojects { tasks.register("clean") { delete(rootProject.layout.buildDirectory) } -android { - ndkVersion = "27.0.12077973" - ... -} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 879d04c..0d69837 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,35 +4,54 @@ import 'package:flutter/services.dart' show rootBundle; import 'package:window_size/window_size.dart'; import 'package:desktop_window/desktop_window.dart'; import 'package:flutter_native_splash/flutter_native_splash.dart'; - +import 'package:http/http.dart' as http; +import 'package:csv/csv.dart'; void main() { WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); setWindowTitle('하나랜덤식당'); - DesktopWindow.setWindowSize(Size(480,740)); + DesktopWindow.setWindowSize(Size(480, 740)); //FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); FlutterNativeSplash.remove(); runApp(RandomSelectorApp()); } +Future fetchCsvData(String url) async { + try { + final response = await http.get(Uri.parse(url)); + if (response.statusCode == 200) { + return response.body; + } else { + print('Failed to fetch CSV: ${response.statusCode}'); + return ''; // Or throw an error + } + } catch (e) { + print('Error fetching CSV: $e'); + return ''; // Or throw an error + } +} class RandomSelectorApp extends StatelessWidget { @override Widget build(BuildContext context) { - return MaterialApp( - title: '명동 식당고르기', - home: RandomSelectorScreen(), - ); + return MaterialApp(title: '명동 식당고르기', home: RandomSelectorScreen()); } } class RandomSelectorScreen extends StatefulWidget { + final String csvUrl = + 'https://raw.githubusercontent.com/rl544/HanaRandomFood/refs/heads/main/assets/list.csv'; // Replace with the actual URL + @override _RandomSelectorScreenState createState() => _RandomSelectorScreenState(); } -class _RandomSelectorScreenState extends State with SingleTickerProviderStateMixin { +class _RandomSelectorScreenState extends State + with SingleTickerProviderStateMixin { List items = []; + List _csvData = []; // List> _csvData = []; + List> _csvDataWithHeader = []; + // List? selectedItem; String? selectedItem; int _key = 0; Color _color1 = const Color.fromARGB(255, 242, 240, 245); @@ -40,6 +59,7 @@ class _RandomSelectorScreenState extends State with Single late AnimationController _controller; late Animation _rotationAnimation; bool _loading = true; + String _errorMessage = ''; @override void initState() { @@ -48,24 +68,114 @@ class _RandomSelectorScreenState extends State with Single duration: Duration(milliseconds: 600), vsync: this, ); - _rotationAnimation = Tween(begin: 0, end: 2 * pi).animate( - CurvedAnimation(parent: _controller, curve: Curves.elasticOut), - ); - _loadCSV(); + _rotationAnimation = Tween( + begin: 0, + end: 2 * pi, + ).animate(CurvedAnimation(parent: _controller, curve: Curves.elasticOut)); + _loadCsvData(); // from Web + // _loadCSV(); // from internal } + Future _loadCsvData() async { + setState(() { + _loading = true; + _errorMessage = ''; + }); + final csvString = await fetchCsvData(widget.csvUrl); + if (csvString.isNotEmpty) { + setState(() { + // _csvData = parseCsv(csvString); + _csvData = parseLCsv(csvString); + _csvDataWithHeader = parseCsvWithHeader(csvString); + _loading = false; + }); + } else { + setState(() { + _loading = false; + _errorMessage = 'Failed to load CSV data.'; + }); + } + } + + // @override + // Widget build(BuildContext context) { + // return Scaffold( + // appBar: AppBar(title: Text('CSV Data from Web')), + // body: + // _loading + // ? Center(child: CircularProgressIndicator()) + // : _errorMessage.isNotEmpty + // ? Center(child: Text(_errorMessage)) + // : SingleChildScrollView( + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text( + // 'Raw CSV Data:', + // style: TextStyle(fontWeight: FontWeight.bold), + // ), + // for (var row in _csvData) Text(row.join(', ')), + // SizedBox(height: 20), + // Text( + // 'CSV Data with Header:', + // style: TextStyle(fontWeight: FontWeight.bold), + // ), + // if (_csvDataWithHeader.isNotEmpty) + // for (var row in _csvDataWithHeader) Text(row.toString()), + // if (_csvDataWithHeader.isEmpty && _csvData.isNotEmpty) + // Text('No header row detected (using basic parsing).'), + // if (_csvData.isEmpty && _errorMessage.isEmpty) + // Text('No CSV data loaded yet.'), + // ], + // ), + // ), + // ); + // } + Future _loadCSV() async { final csvString = await rootBundle.loadString('assets/list.csv'); setState(() { - items = csvString - .split('\n') - .map((e) => e.trim()) - .where((e) => e.isNotEmpty) - .toList(); + items = parseLCsv(csvString); _loading = false; }); } + List parseLCsv(String csvString) { + final List ans = + csvString + .split('\n') + .map((e) => e.trim()) + .where((e) => e.isNotEmpty) + .toList(); + return ans; + } + + List> parseCsv(String csvString) { + final List> rowsAsListOfValues = const CsvToListConverter() + .convert(csvString); + return rowsAsListOfValues; + } + + List> parseCsvWithHeader(String csvString) { + final List> rows = const CsvToListConverter().convert( + csvString, + ); + if (rows.isEmpty) { + return []; + } + final List header = + rows.first.map((item) => item.toString()).toList(); + final List> data = []; + for (int i = 1; i < rows.length; i++) { + final Map rowData = {}; + for (int j = 0; j < header.length; j++) { + rowData[header[j]] = rows[i][j]; + } + data.add(rowData); + } + return data; + } + @override void dispose() { _controller.dispose(); @@ -73,13 +183,17 @@ class _RandomSelectorScreenState extends State with Single } void selectRandomItem() { - if (items.isEmpty) return; + if (_csvData.isEmpty) return; // _csvData items final random = Random(); setState(() { - selectedItem = items[random.nextInt(items.length)]; + selectedItem = _csvData[random.nextInt(_csvData.length)]; _key++; - _color1 = Color((random.nextDouble() * 0xFFFFFF).toInt()).withOpacity(0.2); - _color2 = Color((random.nextDouble() * 0xFFFFFF).toInt()).withOpacity(0.2); + _color1 = Color( + (random.nextDouble() * 0xFFFFFF).toInt(), + ).withOpacity(0.2); + _color2 = Color( + (random.nextDouble() * 0xFFFFFF).toInt(), + ).withOpacity(0.2); }); _controller.forward(from: 0); } @@ -87,7 +201,6 @@ class _RandomSelectorScreenState extends State with Single @override Widget build(BuildContext context) { return Scaffold( - body: AnimatedContainer( duration: Duration(milliseconds: 1200), decoration: BoxDecoration( @@ -98,126 +211,151 @@ class _RandomSelectorScreenState extends State with Single ), ), child: Center( - child: _loading - ? CircularProgressIndicator() - : Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: null, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.white, - foregroundColor: Colors.white, - elevation: 6, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), - padding: EdgeInsets.symmetric(horizontal: 32, vertical: 16), - textStyle: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), - ), - child: Text('하나은행 IT시스템부 단말인프라팀', style: TextStyle(color: Colors.white, - shadows: [ - Shadow( - blurRadius: 18.0, - color: Colors.black.withOpacity(0.6), - offset: Offset(0, 0), - ), - Shadow( - blurRadius: 8.0, - color: Colors.black.withOpacity(0.5), - offset: Offset(2, 2), - ), - ], - ), - ), - ), - SizedBox(height: 150), - Padding( - padding: const EdgeInsets.only(bottom: 32.0), - child: Image.asset( - 'assets/images/ask.png', - width: 100, - height: 100, - ), - ), - AnimatedBuilder( - animation: _rotationAnimation, - builder: (context, child) { - return Transform.rotate( - angle: _rotationAnimation.value, - child: child, - ); - }, - child: AnimatedSwitcher( - duration: Duration(milliseconds: 500), - transitionBuilder: (Widget child, Animation animation) { - return ScaleTransition( - scale: animation, - child: FadeTransition( - opacity: animation, - child: child, - ), - ); - }, - child: Text( - selectedItem ?? '오늘의 식당을 골라주세요!', - key: ValueKey(_key), - style: TextStyle( - fontSize: 32, + child: + _loading + ? CircularProgressIndicator() + : Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + onPressed: null, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white, + foregroundColor: Colors.white, + elevation: 6, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + padding: EdgeInsets.symmetric( + horizontal: 32, + vertical: 16, + ), + textStyle: TextStyle( + fontSize: 24, fontWeight: FontWeight.bold, + ), + ), + child: Text( + '하나은행 IT시스템부 단말인프라팀', + style: TextStyle( color: Colors.white, shadows: [ Shadow( blurRadius: 18.0, - color: Colors.black.withOpacity(0.7), + color: Colors.black.withOpacity(0.6), offset: Offset(0, 0), ), Shadow( blurRadius: 8.0, - color: _color1.withOpacity(0.7), + color: Colors.black.withOpacity(0.5), offset: Offset(2, 2), ), ], ), - textAlign: TextAlign.center, ), ), - ), - SizedBox(height: 50), - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: const Color.fromARGB(255, 101, 177, 130), - foregroundColor: _color1, - elevation: 10, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(30), + SizedBox(height: 150), + Padding( + padding: const EdgeInsets.only(bottom: 32.0), + child: Image.asset( + 'assets/images/ask.png', + width: 100, + height: 100, ), - padding: EdgeInsets.symmetric(horizontal: 32, vertical: 16), - textStyle: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), - onPressed: selectRandomItem, - child: Text('어디로 갈까요?', - style: TextStyle( - color: Colors.white, - shadows: [ - Shadow( - blurRadius: 18.0, - color: Colors.black.withOpacity(0.4), - offset: Offset(0, 0), + AnimatedBuilder( + animation: _rotationAnimation, + builder: (context, child) { + return Transform.rotate( + angle: _rotationAnimation.value, + child: child, + ); + }, + child: AnimatedSwitcher( + duration: Duration(milliseconds: 500), + transitionBuilder: ( + Widget child, + Animation animation, + ) { + return ScaleTransition( + scale: animation, + child: FadeTransition( + opacity: animation, + child: child, + ), + ); + }, + child: Text( + selectedItem ?? '오늘의 식당을 골라주세요!', + key: ValueKey(_key), + style: TextStyle( + fontSize: 32, + fontWeight: FontWeight.bold, + color: Colors.white, + shadows: [ + Shadow( + blurRadius: 18.0, + color: Colors.black.withOpacity(0.7), + offset: Offset(0, 0), + ), + Shadow( + blurRadius: 8.0, + color: _color1.withOpacity(0.7), + offset: Offset(2, 2), + ), + ], ), - Shadow( - blurRadius: 8.0, - color: _color1.withOpacity(0.5), - offset: Offset(2, 2), - ), - ], + textAlign: TextAlign.center, + ), ), ), - ), - ], - ), + SizedBox(height: 50), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: const Color.fromARGB( + 255, + 101, + 177, + 130, + ), + foregroundColor: _color1, + elevation: 10, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30), + ), + padding: EdgeInsets.symmetric( + horizontal: 32, + vertical: 16, + ), + textStyle: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + onPressed: selectRandomItem, + child: Text( + '어디로 갈까요?', + style: TextStyle( + color: Colors.white, + shadows: [ + Shadow( + blurRadius: 18.0, + color: Colors.black.withOpacity(0.4), + offset: Offset(0, 0), + ), + Shadow( + blurRadius: 8.0, + color: _color1.withOpacity(0.5), + offset: Offset(2, 2), + ), + ], + ), + ), + ), + ], + ), ), ), ); } -} \ No newline at end of file +} diff --git a/pubspec.lock b/pubspec.lock index 21c0b11..093fefc 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -184,6 +184,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.15.6" + http: + dependency: "direct main" + description: + name: http + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" image: dependency: transitive description: @@ -365,6 +381,14 @@ packages: url: "https://pub.dev" source: hosted version: "14.3.1" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" window_size: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 746d063..0c9c5ba 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,6 +34,7 @@ dependencies: # 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.1 csv: ^6.0.0 flutter_launcher_icons: ^0.14.3 flutter_native_splash: ^2.4.6