diff --git a/packages/multi_trigger_autocomplete_plus/example/pubspec.lock b/packages/multi_trigger_autocomplete_plus/example/pubspec.lock index 5bfc05a8b..e6a8b0155 100644 --- a/packages/multi_trigger_autocomplete_plus/example/pubspec.lock +++ b/packages/multi_trigger_autocomplete_plus/example/pubspec.lock @@ -5,50 +5,50 @@ packages: dependency: transitive description: name: async - sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.12.0" + version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" characters: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" clock: dependency: transitive description: name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" collection: dependency: transitive description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "1.17.2" crypto: dependency: transitive description: name: crypto - sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.0.3" cupertino_icons: dependency: "direct main" description: @@ -61,18 +61,18 @@ packages: dependency: transitive description: name: fake_async - sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.3.1" ffi: dependency: transitive description: name: ffi - sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" + sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.0" flutter: dependency: "direct main" description: flutter @@ -119,10 +119,10 @@ packages: dependency: transitive description: name: http - sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.1.0" http_parser: dependency: transitive description: @@ -131,30 +131,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec - url: "https://pub.dev" - source: hosted - version: "10.0.8" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 - url: "https://pub.dev" - source: hosted - version: "3.0.9" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://pub.dev" - source: hosted - version: "3.0.1" lints: dependency: transitive description: @@ -167,26 +143,26 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.5.0" meta: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.9.1" multi_trigger_autocomplete_plus: dependency: "direct main" description: @@ -198,34 +174,34 @@ packages: dependency: transitive description: name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.8.3" path_provider: dependency: transitive description: name: path_provider - sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.1.3" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2" + sha256: "51f0d2c554cfbc9d6a312ab35152fc77e2f0b758ce9f1a444a3a1e5b8f3c6b7f" url: "https://pub.dev" source: hosted - version: "2.2.15" + version: "2.2.3" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.3.2" path_provider_linux: dependency: transitive description: @@ -246,18 +222,18 @@ packages: dependency: transitive description: name: path_provider_windows - sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.2.1" platform: dependency: transitive description: name: platform - sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" url: "https://pub.dev" source: hosted - version: "3.1.6" + version: "3.1.4" plugin_platform_interface: dependency: transitive description: @@ -270,63 +246,63 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" source_span: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.6.0" typed_data: dependency: transitive description: name: typed_data - sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.2" vector_math: dependency: transitive description: @@ -335,30 +311,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - vm_service: + web: dependency: transitive description: - name: vm_service - sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 url: "https://pub.dev" source: hosted - version: "14.3.1" - web: + version: "0.1.4-beta" + win32: dependency: transitive description: - name: web - sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb + name: win32 + sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574 url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "5.1.1" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.0.4" sdks: - dart: ">=3.7.0-0 <4.0.0" - flutter: ">=3.24.0" + dart: ">=3.1.0 <4.0.0" + flutter: ">=3.13.0" diff --git a/prototypes/json_ui_demo/.gitignore b/prototypes/json_ui_demo/.gitignore new file mode 100644 index 000000000..24476c5d1 --- /dev/null +++ b/prototypes/json_ui_demo/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/prototypes/json_ui_demo/README.md b/prototypes/json_ui_demo/README.md new file mode 100644 index 000000000..b69ad0446 --- /dev/null +++ b/prototypes/json_ui_demo/README.md @@ -0,0 +1,16 @@ +# json_ui_demo + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/prototypes/json_ui_demo/analysis_options.yaml b/prototypes/json_ui_demo/analysis_options.yaml new file mode 100644 index 000000000..0d2902135 --- /dev/null +++ b/prototypes/json_ui_demo/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/prototypes/json_ui_demo/assets/test_orders.json b/prototypes/json_ui_demo/assets/test_orders.json new file mode 100644 index 000000000..caf2f9960 --- /dev/null +++ b/prototypes/json_ui_demo/assets/test_orders.json @@ -0,0 +1,16 @@ +[ + { + "orderId": "ORD001", + "customer": { + "name": "Charlie", + "phone": "123-456-7890" + }, + "items": [ + {"name": "Notebook", "qty": 2, "price": 5.99}, + {"name": "Pen", "qty": 3, "price": 1.99} + ], + "total": 17.95, + "paid": true + } + ] + \ No newline at end of file diff --git a/prototypes/json_ui_demo/assets/test_posts.json b/prototypes/json_ui_demo/assets/test_posts.json new file mode 100644 index 000000000..387cd7175 --- /dev/null +++ b/prototypes/json_ui_demo/assets/test_posts.json @@ -0,0 +1,15 @@ +[ + { + "title": "Introducing Flutter", + "author": { + "name": "Jane Doe", + "email": "jane@example.com" + }, + "content": "Flutter is Google's UI toolkit...", + "tags": ["flutter", "mobile", "google"], + "views": 1234, + "published": true, + "created_at": "2024-01-12T10:00:00Z" + } + ] + \ No newline at end of file diff --git a/prototypes/json_ui_demo/assets/test_products.json b/prototypes/json_ui_demo/assets/test_products.json new file mode 100644 index 000000000..f3b6f00c8 --- /dev/null +++ b/prototypes/json_ui_demo/assets/test_products.json @@ -0,0 +1,22 @@ +[ + { + "productId": "A100", + "name": "Laptop", + "price": 899.99, + "tags": ["electronics", "portable"], + "stock": { + "warehouse": 15, + "retail": 8 + } + }, + { + "productId": "B200", + "name": "Headphones", + "price": 199.99, + "tags": ["audio", "wireless"], + "stock": { + "warehouse": 30, + "retail": 12 + } + } + ] \ No newline at end of file diff --git a/prototypes/json_ui_demo/assets/test_url.json b/prototypes/json_ui_demo/assets/test_url.json new file mode 100644 index 000000000..18e51a33a --- /dev/null +++ b/prototypes/json_ui_demo/assets/test_url.json @@ -0,0 +1,19 @@ +[ + { + "id": 1, + "name": "Alice", + "email": "alice@example.com", + "profileUrl": "https://example.com/alice", + "isActive": true, + "createdAt": "2024-01-01T00:00:00Z" + }, + { + "id": 2, + "name": "Bob", + "email": "bob@example.com", + "profileUrl": "https://example.com/bob", + "isActive": false, + "createdAt": "2024-01-02T00:00:00Z" + } + ] + \ No newline at end of file diff --git a/prototypes/json_ui_demo/assets/test_users.json b/prototypes/json_ui_demo/assets/test_users.json new file mode 100644 index 000000000..f3754570a --- /dev/null +++ b/prototypes/json_ui_demo/assets/test_users.json @@ -0,0 +1,16 @@ +[ + { + "id": 1, + "name": "Alice", + "email": "alice@example.com", + "isActive": true, + "createdAt": "2024-03-01T12:30:00Z" + }, + { + "id": 2, + "name": "Bob", + "email": "bob@example.com", + "isActive": false, + "createdAt": "2023-11-15T09:10:00Z" + } +] \ No newline at end of file diff --git a/prototypes/json_ui_demo/lib/llm_service.dart b/prototypes/json_ui_demo/lib/llm_service.dart new file mode 100644 index 000000000..6dda02217 --- /dev/null +++ b/prototypes/json_ui_demo/lib/llm_service.dart @@ -0,0 +1,112 @@ +import 'dart:convert'; +import 'package:http/http.dart' as http; +import 'schema_parser.dart'; +import 'package:flutter/foundation.dart'; // For debugPrint + +class LLMService { + final String apiKey; + + LLMService(this.apiKey); + + Future> getWidgetSuggestions(Schema schema) async { + final prompt = _buildPrompt(schema); + + debugPrint('[DEBUG] ===== LLM API Request Started ====='); + debugPrint('[DEBUG] Prompt:\n$prompt'); + + try { + final response = await http.post( + Uri.parse("https://api.openai.com/v1/chat/completions"), + headers: { + 'Authorization': 'Bearer $apiKey', + 'Content-Type': 'application/json', + }, + body: jsonEncode({ + "model": "gpt-3.5-turbo", + "messages": [ + { + "role": "system", + "content": "You are a UI assistant that recommends Flutter widget types based on field name and data type." + }, + { + "role": "user", + "content": prompt, + } + ], + "temperature": 0.2, + }), + ); + + debugPrint('[DEBUG] Status code: ${response.statusCode}'); + debugPrint('[DEBUG] Response body:\n${response.body}'); + + if (response.statusCode == 200) { + final content = jsonDecode(response.body); + final message = content['choices'][0]['message']['content']; + debugPrint('[DEBUG] Message content:\n$message'); + + final cleaned = _extractJson(message); + final decoded = jsonDecode(cleaned); + + if (decoded['fields'] == null || decoded['fields'] is! List) { + throw Exception('Missing or invalid "fields" in GPT response'); + } + + List updated = []; + for (var f in decoded['fields']) { + updated.add(FieldSchema( + key: f['key'], + type: f['type'], + suggestedWidget: f['suggestedWidget'], + label: f['label'], + hintText: f['hintText'], + icon: f['icon'], + )); + } + + return updated; + } else { + throw Exception('Failed to fetch GPT suggestion: ${response.body}'); + } + } catch (e) { + debugPrint('[WARN] LLM failed, fallback: $e'); + rethrow; + } + } + + String _extractJson(String raw) { + final start = raw.indexOf('{'); + final end = raw.lastIndexOf('}'); + if (start != -1 && end != -1 && end > start) { + return raw.substring(start, end + 1); + } + throw Exception('Could not extract valid JSON from GPT response'); + } + + String _buildPrompt(Schema schema) { + return ''' +You are an expert Flutter UI designer. Given a field schema, your task is to: +- Suggest a widget that best represents the field +- Add field-level customization (label, hintText, icon if applicable) +- Group related fields into sections if relevant +- Format response as valid JSON that includes these details. + +Schema: +${jsonEncode(schema.toJson())} + +Please return in this format (only return in this format): +{ + "fields": [ + { + "key": "email", + "type": "string", + "suggestedWidget": "TextField", + "label": "Email Address", + "hintText": "user@example.com", + "icon": "email" + } + ] +} +'''; + } +} diff --git a/prototypes/json_ui_demo/lib/main.dart b/prototypes/json_ui_demo/lib/main.dart new file mode 100644 index 000000000..02a248f04 --- /dev/null +++ b/prototypes/json_ui_demo/lib/main.dart @@ -0,0 +1,135 @@ +import 'dart:convert'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show rootBundle; + +import 'schema_parser.dart'; +import 'llm_service.dart'; +import 'widget_factory.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Schema Extractor', + home: SchemaExtractorPage(), + ); + } +} + +class SchemaExtractorPage extends StatefulWidget { + const SchemaExtractorPage({super.key}); + @override + State createState() => _SchemaExtractorPageState(); +} + +class _SchemaExtractorPageState extends State { + String status = 'Loading...'; + Schema? _schema; + + final llm = LLMService('sk-xxxx'); // GPT API Key + + @override + void initState() { + super.initState(); + _loadAndParse(); + } + + Future _loadAndParse() async { + try { + String jsonString = await rootBundle.loadString('assets/test_users.json'); + List data = jsonDecode(jsonString); + + if (data.isNotEmpty && data.first is Map) { + Schema schema = parseJsonToSchema(data.first); + print('[DEBUG] Parsed schema (before LLM): ${jsonEncode(schema.toJson())}'); + + try { + // try GPT generate widget + List aiEnhanced = await llm.getWidgetSuggestions(schema); + setState(() { + _schema = Schema(type: schema.type, fields: aiEnhanced); + status = 'AI-enhanced schema loaded'; + }); + } catch (e) { + // fallback:rulebased inferWidget + print('[WARN] LLM failed, fallback: $e'); + List fallbackFields = schema.fields.map((f) { + return FieldSchema( + key: f.key, + type: f.type, + suggestedWidget: inferWidget(f), + ); + }).toList(); + setState(() { + _schema = Schema(type: schema.type, fields: fallbackFields); + status = 'LLM failed. Using fallback widget mapping'; + }); + } + } else { + setState(() { + status = 'Invalid JSON format.'; + }); + } + } catch (e) { + setState(() { + status = 'Error: $e'; + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.grey[50], + appBar: AppBar( + title: const Text('AI Form Generator'), + centerTitle: true, + ), + body: Padding( + padding: const EdgeInsets.all(16), + child: _schema == null + ? Center(child: Text(status)) + : SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + '🧠 Detected Fields:', + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 12), + ..._schema!.fields.map((field) => Card( + child: ListTile( + leading: const Icon(Icons.label_outline), + title: Text(field.key, style: const TextStyle(fontWeight: FontWeight.bold)), + subtitle: Text('Type: ${field.type}\nWidget: ${field.suggestedWidget}'), + ), + )), + const SizedBox(height: 32), + const Divider(), + const Text( + '🧩 Rendered UI Preview:', + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 16), + Card( + elevation: 1, + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + children: WidgetFactory.buildWidgetsFromSchema(_schema!), + ), + ), + ), + ], + ), + ), + ), + ); + } + } diff --git a/prototypes/json_ui_demo/lib/schema_parser.dart b/prototypes/json_ui_demo/lib/schema_parser.dart new file mode 100644 index 000000000..bd107d3c9 --- /dev/null +++ b/prototypes/json_ui_demo/lib/schema_parser.dart @@ -0,0 +1,115 @@ +class FieldSchema { + final String key; + final String type; + final String suggestedWidget; + final String? label; + final String? hintText; + final String? icon; + + FieldSchema({ + required this.key, + required this.type, + required this.suggestedWidget, + this.label, + this.hintText, + this.icon, + }); + + Map toJson() => { + 'key': key, + 'type': type, + 'suggestedWidget': suggestedWidget, + if (label != null) 'label': label, + if (hintText != null) 'hintText': hintText, + if (icon != null) 'icon': icon, + }; + + factory FieldSchema.fromJson(Map json) { + return FieldSchema( + key: json['key'], + type: json['type'], + suggestedWidget: json['suggestedWidget'], + label: json['label'], + hintText: json['hintText'], + icon: json['icon'], + ); + } +} + +class Schema { + final String type; + final List fields; + + Schema({required this.type, required this.fields}); + + Map toJson() => { + 'type': type, + 'fields': fields.map((f) => f.toJson()).toList(), + }; + + factory Schema.fromJson(Map json) { + return Schema( + type: json['type'], + fields: (json['fields'] as List) + .map((f) => FieldSchema.fromJson(f)) + .toList(), + ); + } +} + +// backup plan rule based +String inferWidget(FieldSchema field) { + final key = field.key.toLowerCase(); + final type = field.type; + + if (type == 'boolean') return 'Switch'; + if (type == 'number') return 'Text'; + if (type == 'string') { + if (key.contains('email')) return 'EmailField'; + if (key.contains('url') || key.contains('link')) return 'UrlField'; + return 'Text'; + } + if (type == 'array') return 'ListView'; + if (type == 'object') return 'Card'; + + return 'Text'; +} + +Schema parseJsonToSchema(Map jsonObj) { + List fields = []; + + jsonObj.forEach((key, value) { + String fieldType; + + if (value is String) { + fieldType = 'string'; + } else if (value is int || value is double) { + fieldType = 'number'; + } else if (value is bool) { + fieldType = 'boolean'; + } else if (value is List) { + fieldType = 'array'; + } else if (value is Map) { + fieldType = 'object'; + } else { + fieldType = 'unknown'; + } + + String widget = inferWidget(FieldSchema( + key: key, + type: fieldType, + suggestedWidget: '', + )); + + fields.add(FieldSchema( + key: key, + type: fieldType, + suggestedWidget: widget, + label: key, + hintText: null, + icon: null, + )); + }); + + return Schema(type: 'object', fields: fields); +} \ No newline at end of file diff --git a/prototypes/json_ui_demo/lib/test_openai_call.dart b/prototypes/json_ui_demo/lib/test_openai_call.dart new file mode 100644 index 000000000..2a9e69cd8 --- /dev/null +++ b/prototypes/json_ui_demo/lib/test_openai_call.dart @@ -0,0 +1,32 @@ +import 'dart:convert'; +import 'package:http/http.dart' as http; + +void main() async { + const apiKey = 'sk-xxx'; + + final url = Uri.parse('https://api.openai.com/v1/chat/completions'); + + final headers = { + 'Authorization': 'Bearer $apiKey', + 'Content-Type': 'application/json', + }; + + final body = jsonEncode({ + "model": "gpt-3.5-turbo", + "messages": [ + {"role": "user", "content": "Hello! What is Flutter?"} + ], + "temperature": 0.7 + }); + + print('[DEBUG] Sending request...'); + try { + final response = await http.post(url, headers: headers, body: body); + + print('[DEBUG] Status code: ${response.statusCode}'); + print('[DEBUG] Response body:'); + print(response.body); + } catch (e) { + print('[ERROR] Failed to connect: $e'); + } +} diff --git a/prototypes/json_ui_demo/lib/widget_factory.dart b/prototypes/json_ui_demo/lib/widget_factory.dart new file mode 100644 index 000000000..adf35b859 --- /dev/null +++ b/prototypes/json_ui_demo/lib/widget_factory.dart @@ -0,0 +1,113 @@ +import 'package:flutter/material.dart'; +import 'schema_parser.dart'; + +class WidgetFactory { + static Widget buildWidget(FieldSchema field) { + final label = field.label ?? field.key; + final widgetType = field.suggestedWidget; + + return Card( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + elevation: 3, + margin: const EdgeInsets.symmetric(vertical: 10), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label, + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 10), + _buildFieldByType(widgetType, field), + if (field.hintText != null && field.hintText!.isNotEmpty) + Padding( + padding: const EdgeInsets.only(top: 6), + child: Text( + field.hintText!, + style: const TextStyle(fontSize: 12, color: Colors.grey), + ), + ), + ], + ), + ), + ); + } + + static Widget _buildFieldByType(String widgetType, FieldSchema field) { + switch (widgetType) { + case 'TextField': + case 'Text': + return TextField( + decoration: InputDecoration( + border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)), + prefixIcon: field.icon != null ? Icon(_getIconData(field.icon!)) : null, + ), + ); + case 'Switch': + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + field.label ?? field.key, + style: const TextStyle(fontSize: 14), + ), + Switch(value: false, onChanged: (v) {}), + ], + ); + case 'EmailField': + return TextField( + decoration: InputDecoration( + border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)), + prefixIcon: const Icon(Icons.email), + ), + keyboardType: TextInputType.emailAddress, + ); + case 'UrlField': + return TextField( + decoration: InputDecoration( + border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)), + prefixIcon: const Icon(Icons.link), + ), + keyboardType: TextInputType.url, + ); + case 'ListView': + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [Text('[List of items]')], + ); + case 'Card': + return Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: Colors.grey[100], + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.grey[300]!), + ), + child: Text('{ ...object... }'), + ); + default: + return Text('[Unsupported widget type]'); + } + } + + static List buildWidgetsFromSchema(Schema schema) { + return schema.fields + .map((f) => buildWidget(f)) + .toList(); + } + + static IconData _getIconData(String iconName) { + switch (iconName.toLowerCase()) { + case 'email': + return Icons.email; + case 'link': + return Icons.link; + case 'phone': + return Icons.phone; + default: + return Icons.device_unknown; + } + } +} diff --git a/prototypes/json_ui_demo/pubspec.lock b/prototypes/json_ui_demo/pubspec.lock new file mode 100644 index 000000000..758c687f6 --- /dev/null +++ b/prototypes/json_ui_demo/pubspec.lock @@ -0,0 +1,212 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + url: "https://pub.dev" + source: hosted + version: "1.17.2" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.dev" + source: hosted + version: "1.0.8" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + url: "https://pub.dev" + source: hosted + version: "2.0.3" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + http: + dependency: "direct main" + description: + name: http + sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" + url: "https://pub.dev" + source: hosted + version: "0.13.6" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + lints: + dependency: transitive + description: + name: lints + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + url: "https://pub.dev" + source: hosted + version: "0.12.16" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + url: "https://pub.dev" + source: hosted + version: "0.5.0" + meta: + dependency: transitive + description: + name: meta + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path: + dependency: transitive + description: + name: path + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" + source: hosted + version: "1.8.3" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" + source: hosted + version: "1.11.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + url: "https://pub.dev" + source: hosted + version: "0.6.0" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" +sdks: + dart: ">=3.1.3 <4.0.0" diff --git a/prototypes/json_ui_demo/pubspec.yaml b/prototypes/json_ui_demo/pubspec.yaml new file mode 100644 index 000000000..0cbde1eba --- /dev/null +++ b/prototypes/json_ui_demo/pubspec.yaml @@ -0,0 +1,97 @@ +name: json_ui_demo +description: A new Flutter project. +# 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.1.3 <4.0.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 + http: ^0.13.6 + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.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: ^2.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 + + assets: + - assets/test_users.json + - assets/test_products.json + - assets/test_posts.json + - assets/test_orders.json + - assets/test_url.json + # 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/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # 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/custom-fonts/#from-packages diff --git a/prototypes/json_ui_demo/test/widget_test.dart b/prototypes/json_ui_demo/test/widget_test.dart new file mode 100644 index 000000000..69a07ea24 --- /dev/null +++ b/prototypes/json_ui_demo/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:json_ui_demo/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +}