From 16d0ddca78740e35c7ce41ae96e8d9b31b62763f Mon Sep 17 00:00:00 2001 From: Sergey Elpashev Date: Wed, 1 May 2024 20:17:20 +0300 Subject: [PATCH] Goods details page --- lib/components/card.dart | 2 +- lib/main.dart | 178 ++++++++---------- lib/pages/basket.dart | 21 +++ lib/pages/detail.dart | 93 +++++++++ lib/pages/order_history.dart | 21 +++ linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.yaml | 1 + web/css/styles.css | 4 +- .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 12 files changed, 231 insertions(+), 100 deletions(-) create mode 100644 lib/pages/basket.dart create mode 100644 lib/pages/detail.dart create mode 100644 lib/pages/order_history.dart diff --git a/lib/components/card.dart b/lib/components/card.dart index 632a76c..fd83d48 100644 --- a/lib/components/card.dart +++ b/lib/components/card.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; class ProductCard extends StatelessWidget { final Image imagePath; final String name; - final int price; + final String price; final VoidCallback onTap; const ProductCard({ diff --git a/lib/main.dart b/lib/main.dart index 06fe723..de10f6b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,14 +4,16 @@ import 'dart:js_interop_unsafe' as js_util; import 'package:flutter/material.dart'; import 'package:flutter_application_1/components/card.dart'; +import 'package:flutter_application_1/pages/basket.dart'; +import 'package:flutter_application_1/pages/detail.dart'; +import 'package:flutter_application_1/pages/order_history.dart'; import 'package:flutter_application_1/theme.dart'; +import 'package:url_launcher/url_launcher.dart'; void main() { runApp(const MyApp()); } -enum GymLinkScreen { mainPage, basketPage, historyPage } - class MyApp extends StatefulWidget { const MyApp({super.key}); @@ -20,10 +22,47 @@ class MyApp extends StatefulWidget { State createState() => _MyAppState(); } +const List> testData = [ + { + "name": "Протеин", + "image": "product.png", + "price": "120", + "details": "Test details", + "id": "34fa3126-bfaf-5dec-8f4a-b246c097ef73" + }, + { + "name": "Протеин", + "image": "product.png", + "price": "150", + "details": "Test details", + "id": "34a26e82-7656-5e98-a44a-c2d01d0b1ad1123" + }, + { + "name": "Протеин", + "image": "product.png", + "price": "250", + "details": "Test details", + "id": "4fb204b7-3f9e-52a2-bed1-415c00a31a37123" + }, + { + "name": "Протеин", + "image": "product.png", + "price": "300", + "details": "Test details", + "id": "09b2f5bb-683e-5c39-ae89-b8e152fa8bcf123" + }, + { + "name": "Протеин", + "image": "product.png", + "price": "100", + "details": "Test details", + "id": "cd1b6817-db94-5394-be1d-af88af79749f123" + } +]; + @js.JSExport() class _MyAppState extends State { final _streamController = StreamController.broadcast(); - GymLinkScreen _currentGymLinkScreen = GymLinkScreen.mainPage; bool _isLoading = true; @override @@ -46,7 +85,7 @@ class _MyAppState extends State { title: 'GymLink Module', theme: myTheme, debugShowCheckedModeBanner: false, - home: gymLinkScreenRouter(_currentGymLinkScreen), + home: MainPage(isLoading: _isLoading), ); } @@ -58,55 +97,28 @@ class _MyAppState extends State { }); } } - - Widget gymLinkScreenRouter(GymLinkScreen which) { - switch (which) { - case GymLinkScreen.mainPage: - return MainPage( - title: 'Counter', - isLoading: _isLoading, - changeGymLinkScreenTo: changeGymLinkScreenTo); - case GymLinkScreen.basketPage: - return BasketPage(changeGymLinkScreenTo: changeGymLinkScreenTo); - case GymLinkScreen.historyPage: - return HistoryPage(changeGymLinkScreenTo: changeGymLinkScreenTo); - } - } - - @js.JSExport() - void changeGymLinkScreenTo(String screenString) { - setState(() { - switch (screenString) { - case 'main': - _currentGymLinkScreen = GymLinkScreen.mainPage; - break; - case 'basket': - _currentGymLinkScreen = GymLinkScreen.basketPage; - break; - case 'history': - _currentGymLinkScreen = GymLinkScreen.historyPage; - break; - } - }); - } } class MainPage extends StatefulWidget { - final String title; final bool isLoading; - final void Function(String) changeGymLinkScreenTo; - const MainPage( - {super.key, - required this.title, - required this.isLoading, - required this.changeGymLinkScreenTo}); + const MainPage({ + super.key, + required this.isLoading, + }); @override State createState() => _MainPageState(); } class _MainPageState extends State { + Future _goToPage() async { + final Uri url = Uri.parse('https://google.com'); + if (!await launchUrl(url, webOnlyWindowName: '_blank')) { + throw 'Could not launch $url'; + } + } + @override Widget build(BuildContext context) { return Scaffold( @@ -153,9 +165,7 @@ class _MainPageState extends State { suffixIcon: Padding( padding: const EdgeInsets.only(right: 8), child: ElevatedButton( - onPressed: () { - debugPrint('search button pressed'); - }, + onPressed: _goToPage, style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric( vertical: 8, horizontal: 0), @@ -178,9 +188,9 @@ class _MainPageState extends State { const SizedBox(width: 8), ElevatedButton( onPressed: () { - setState(() { - widget.changeGymLinkScreenTo('basket'); - }); + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => const BasketPage(), + )); }, style: ElevatedButton.styleFrom( padding: const EdgeInsets.all(0), @@ -202,9 +212,9 @@ class _MainPageState extends State { const SizedBox(width: 8), ElevatedButton( onPressed: () { - setState(() { - widget.changeGymLinkScreenTo('history'); - }); + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => const HistoryPage(), + )); }, style: ElevatedButton.styleFrom( padding: const EdgeInsets.all(0), @@ -228,20 +238,32 @@ class _MainPageState extends State { ), Expanded( child: GridView.builder( - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: + (MediaQuery.sizeOf(context).width ~/ 300).floor(), ), - itemCount: 10, + itemCount: testData.length, itemBuilder: (context, index) { + final product = testData[index]; + debugPrint(product.toString()); return ProductCard( imagePath: Image.asset( - 'product.png', + product['image']!, width: 100, ), - name: 'Product $index', - price: 100, - onTap: () => debugPrint('product $index pressed'), + name: product['name']!, + price: product['price']!, + onTap: () => Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => DetailPage( + name: product['name']!, + description: product['details']!, + price: product['price']!, + id: product['id']!, + image: Image.asset(product['image']!, width: 300), + ), + ), + ), ); }, ), @@ -251,41 +273,3 @@ class _MainPageState extends State { ); } } - -class BasketPage extends StatelessWidget { - final void Function(String) changeGymLinkScreenTo; - const BasketPage({super.key, required this.changeGymLinkScreenTo}); - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - leading: IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: () => changeGymLinkScreenTo('main'), - ), - title: const Text('Корзина'), - ), - body: const Center( - child: Text('Корзина'), - )); - } -} - -class HistoryPage extends StatelessWidget { - final void Function(String) changeGymLinkScreenTo; - const HistoryPage({super.key, required this.changeGymLinkScreenTo}); - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - leading: IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: () => changeGymLinkScreenTo('main'), - ), - title: const Text('История заказов'), - ), - body: const Center( - child: Text('История заказов'), - )); - } -} diff --git a/lib/pages/basket.dart b/lib/pages/basket.dart new file mode 100644 index 0000000..0479582 --- /dev/null +++ b/lib/pages/basket.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +class BasketPage extends StatelessWidget { + const BasketPage({ + super.key, + }); + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + leading: IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () => Navigator.pop(context), + ), + title: const Text('Корзина'), + ), + body: const Center( + child: Text('Корзина'), + )); + } +} diff --git a/lib/pages/detail.dart b/lib/pages/detail.dart new file mode 100644 index 0000000..8c52417 --- /dev/null +++ b/lib/pages/detail.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; + +class DetailPage extends StatelessWidget { + final String name; + final String description; + final String price; + final String id; + final Image image; + const DetailPage( + {super.key, + required this.name, + required this.description, + required this.price, + required this.id, + required this.image}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + leading: IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () => Navigator.pop(context), + ), + title: Text('$name - $id'), + ), + body: Padding( + padding: const EdgeInsets.all(20), + child: SizedBox( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + image, + Padding( + padding: const EdgeInsetsDirectional.fromSTEB(0, 60, 60, 60), + child: SizedBox( + width: 340, + height: MediaQuery.sizeOf(context).height, + child: Card( + elevation: 4, + color: const Color(0xFFF2F3F9), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: Padding( + padding: + const EdgeInsetsDirectional.fromSTEB(20, 15, 10, 15), + child: Text( + description, + style: Theme.of(context).textTheme.bodyMedium, + ), + ), + ), + ), + ), + Align( + alignment: const AlignmentDirectional(0, -1), + child: Padding( + padding: const EdgeInsetsDirectional.fromSTEB(0, 60, 0, 0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Стоимость $price', + style: Theme.of(context).textTheme.bodyLarge, + ), + ElevatedButton( + onPressed: () => {}, + style: ElevatedButton.styleFrom( + backgroundColor: Theme.of(context).primaryColor, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(50), + ), + ), + foregroundColor: Colors.white, + padding: const EdgeInsetsDirectional.fromSTEB( + 34, 10, 34, 10)), + child: const Text('Добавить в корзину'), + ) + ], + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/order_history.dart b/lib/pages/order_history.dart new file mode 100644 index 0000000..f32a18d --- /dev/null +++ b/lib/pages/order_history.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +class HistoryPage extends StatelessWidget { + const HistoryPage({ + super.key, + }); + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + leading: IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () => Navigator.pop(context), + ), + title: const Text('История заказов'), + ), + body: const Center( + child: Text('История заказов'), + )); + } +} diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index e71a16d..f6f23bf 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,10 @@ #include "generated_plugin_registrant.h" +#include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 2e1de87..f16b4c3 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index cccf817..8236f57 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,8 @@ import FlutterMacOS import Foundation +import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/pubspec.yaml b/pubspec.yaml index 5f65fc0..8dd0598 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,6 +35,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.6 + url_launcher: ^6.2.6 dev_dependencies: flutter_test: diff --git a/web/css/styles.css b/web/css/styles.css index 5025e30..22b2c8b 100644 --- a/web/css/styles.css +++ b/web/css/styles.css @@ -1,7 +1,7 @@ #flutter_target { border: 1px solid #aaa; - width: 480px; - height: 600px; + width: 80vw; + height: 60vh; border-radius: 0px; transition: all 150ms ease-in; align-self: center; diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 8b6d468..4f78848 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,9 @@ #include "generated_plugin_registrant.h" +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index b93c4c3..88b22e5 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST