Compare commits
20 Commits
master
...
75bfc7ea6b
| Author | SHA1 | Date | |
|---|---|---|---|
| 75bfc7ea6b | |||
| 57ff8a59e8 | |||
| b4092837d2 | |||
| 0c11883b48 | |||
| 4853f61da2 | |||
| 9d92dfd145 | |||
| 4b16b74d15 | |||
| fecc388e1c | |||
| e4628e977f | |||
| 4bbe7fbc0b | |||
| 5c3da0964a | |||
| 26f822e83a | |||
| 8805b2a9a0 | |||
| ff29598ec5 | |||
| 727c04d368 | |||
| 0a491ca34b | |||
| 16d0ddca78 | |||
| f941b26224 | |||
| c6520041a6 | |||
| 7f0cef4b23 |
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"editor.formatOnSave": true
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ if (flutterVersionName == null) {
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
namespace "com.example.flutter_application_1"
|
namespace "com.example.flutter_application_1"
|
||||||
compileSdk flutter.compileSdkVersion
|
compileSdk 34
|
||||||
ndkVersion flutter.ndkVersion
|
ndkVersion flutter.ndkVersion
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
@@ -45,7 +45,7 @@ android {
|
|||||||
applicationId "com.example.flutter_application_1"
|
applicationId "com.example.flutter_application_1"
|
||||||
// You can update the following values to match your application needs.
|
// You can update the following values to match your application needs.
|
||||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||||
minSdkVersion flutter.minSdkVersion
|
minSdkVersion 21
|
||||||
targetSdkVersion flutter.targetSdkVersion
|
targetSdkVersion flutter.targetSdkVersion
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
|
|||||||
BIN
assets/logo.png
Normal file
BIN
assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
BIN
assets/product.png
Normal file
BIN
assets/product.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 455 KiB |
37
lib/components/app_bar.dart
Normal file
37
lib/components/app_bar.dart
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class GymLinkAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||||
|
const GymLinkAppBar({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AppBar(
|
||||||
|
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||||
|
shadowColor: null,
|
||||||
|
automaticallyImplyLeading: false,
|
||||||
|
elevation: 0,
|
||||||
|
scrolledUnderElevation: 4,
|
||||||
|
title: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.only(right: 8),
|
||||||
|
child: Image(
|
||||||
|
image: AssetImage('assets/logo.png'), width: 24, height: 24),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: Text(
|
||||||
|
'Powered by GymLink',
|
||||||
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
toolbarHeight: 30,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Size get preferredSize => const Size.fromHeight(30);
|
||||||
|
}
|
||||||
87
lib/components/basket_item_card.dart
Normal file
87
lib/components/basket_item_card.dart
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class BasketItemCard extends StatelessWidget {
|
||||||
|
final String name;
|
||||||
|
final String price;
|
||||||
|
final String id;
|
||||||
|
final Image image;
|
||||||
|
final String quantity;
|
||||||
|
final VoidCallback onTapPlus;
|
||||||
|
final VoidCallback onTapMinus;
|
||||||
|
|
||||||
|
const BasketItemCard({
|
||||||
|
super.key,
|
||||||
|
required this.name,
|
||||||
|
required this.price,
|
||||||
|
required this.id,
|
||||||
|
required this.image,
|
||||||
|
required this.onTapPlus,
|
||||||
|
required this.quantity,
|
||||||
|
required this.onTapMinus,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsetsDirectional.symmetric(horizontal: 10, vertical: 10),
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
minHeight: 100,
|
||||||
|
maxHeight: 200,
|
||||||
|
minWidth: 400,
|
||||||
|
maxWidth: 600,
|
||||||
|
),
|
||||||
|
child: Card(
|
||||||
|
elevation: 4,
|
||||||
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(30),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.symmetric(horizontal: 20),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
image,
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
name,
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
|
),
|
||||||
|
Text('\$$price'),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.remove),
|
||||||
|
onPressed: onTapMinus,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Text(quantity),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
onPressed: onTapPlus,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
26
lib/components/heading.dart
Normal file
26
lib/components/heading.dart
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class GymLinkHeader extends StatelessWidget {
|
||||||
|
final String title;
|
||||||
|
const GymLinkHeader({super.key, required this.title});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
icon: const Icon(Icons.arrow_back)),
|
||||||
|
Text(title, style: Theme.of(context).textTheme.titleLarge),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Divider(thickness: 1, height: 0),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
79
lib/components/history_item_card.dart
Normal file
79
lib/components/history_item_card.dart
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
|
|
||||||
|
enum OrderStatus { created, inProgress, completed, canceled }
|
||||||
|
|
||||||
|
Map<OrderStatus, String> orderStatusMap = {
|
||||||
|
OrderStatus.created: 'Создан',
|
||||||
|
OrderStatus.inProgress: 'В обработке',
|
||||||
|
OrderStatus.completed: 'Завершен',
|
||||||
|
OrderStatus.canceled: 'Отменен',
|
||||||
|
};
|
||||||
|
|
||||||
|
class HistoryItemCard extends StatelessWidget {
|
||||||
|
final String id;
|
||||||
|
final String cost;
|
||||||
|
final String date;
|
||||||
|
final Image image;
|
||||||
|
final OrderStatus status;
|
||||||
|
|
||||||
|
const HistoryItemCard({
|
||||||
|
super.key,
|
||||||
|
required this.id,
|
||||||
|
required this.cost,
|
||||||
|
required this.date,
|
||||||
|
required this.status,
|
||||||
|
required this.image,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsetsDirectional.symmetric(horizontal: 10, vertical: 10),
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
minHeight: 100,
|
||||||
|
maxHeight: 200,
|
||||||
|
minWidth: 600,
|
||||||
|
maxWidth: 800,
|
||||||
|
),
|
||||||
|
child: Card(
|
||||||
|
elevation: 4,
|
||||||
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(30),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.symmetric(horizontal: 20),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
image,
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
MarkdownBody(
|
||||||
|
data: '### Заказ **№$id** от $date',
|
||||||
|
),
|
||||||
|
MarkdownBody(
|
||||||
|
data: 'Статус: **${orderStatusMap[status]}**'),
|
||||||
|
MarkdownBody(data: 'Сумма: **\$$cost**'),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
58
lib/components/item_card.dart
Normal file
58
lib/components/item_card.dart
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ProductCard extends StatelessWidget {
|
||||||
|
final Image imagePath;
|
||||||
|
final String name;
|
||||||
|
final String price;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
|
||||||
|
const ProductCard({
|
||||||
|
super.key,
|
||||||
|
required this.imagePath,
|
||||||
|
required this.name,
|
||||||
|
required this.price,
|
||||||
|
required this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
double getCardHeight({required BuildContext context}) {
|
||||||
|
if (MediaQuery.of(context).size.width > 600) {
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
minHeight: 80, maxHeight: getCardHeight(context: context)),
|
||||||
|
child: Card(
|
||||||
|
elevation: 3,
|
||||||
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
imagePath,
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
name,
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'\$$price',
|
||||||
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
62
lib/components/order_confirm_item_card.dart
Normal file
62
lib/components/order_confirm_item_card.dart
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
|
|
||||||
|
class OrderConfirmItemCard extends StatelessWidget {
|
||||||
|
final String name;
|
||||||
|
final int count;
|
||||||
|
final double price;
|
||||||
|
final Image image;
|
||||||
|
|
||||||
|
const OrderConfirmItemCard({
|
||||||
|
super.key,
|
||||||
|
required this.name,
|
||||||
|
required this.count,
|
||||||
|
required this.price,
|
||||||
|
required this.image,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsetsDirectional.symmetric(horizontal: 10, vertical: 10),
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(minHeight: 130),
|
||||||
|
child: Card(
|
||||||
|
elevation: 4,
|
||||||
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(30),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.symmetric(horizontal: 20),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
image,
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
name,
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
|
),
|
||||||
|
Text('\$$price x $count = \$${price * count}'),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
MarkdownBody(data: '# X$count')
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
217
lib/main.dart
217
lib/main.dart
@@ -1,226 +1,13 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:js_interop' as js;
|
|
||||||
import 'dart:js_interop_unsafe' as js_util;
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gymlink_module_web/states/web.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
enum DemoScreen { counter, textField }
|
|
||||||
|
|
||||||
class MyApp extends StatefulWidget {
|
class MyApp extends StatefulWidget {
|
||||||
const MyApp({super.key});
|
const MyApp({super.key});
|
||||||
|
|
||||||
// This widget is the root of your application.
|
|
||||||
@override
|
@override
|
||||||
State<MyApp> createState() => _MyAppState();
|
State<MyApp> createState() => MyAppStateWeb();
|
||||||
}
|
|
||||||
|
|
||||||
@js.JSExport()
|
|
||||||
class _MyAppState extends State<MyApp> {
|
|
||||||
final _streanController = StreamController<void>.broadcast();
|
|
||||||
DemoScreen _currentDemoScreen = DemoScreen.counter;
|
|
||||||
int _counterScreenCount = 0;
|
|
||||||
bool _isLoading = true;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
final export = js.createJSInteropWrapper(this);
|
|
||||||
js.globalContext['_appState'] = export;
|
|
||||||
js.globalContext.callMethod('_stateSet'.toJS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_streanController.close();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@js.JSExport()
|
|
||||||
void increment() {
|
|
||||||
if (_currentDemoScreen == DemoScreen.counter) {
|
|
||||||
setState(() {
|
|
||||||
_counterScreenCount++;
|
|
||||||
_streanController.add(null);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@js.JSExport()
|
|
||||||
void addHandler(void Function() handler) {
|
|
||||||
_streanController.stream.listen((event) {
|
|
||||||
handler();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@js.JSExport()
|
|
||||||
int get count => _counterScreenCount;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return MaterialApp(
|
|
||||||
title: 'Aboba app',
|
|
||||||
theme: ThemeData(
|
|
||||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.red),
|
|
||||||
),
|
|
||||||
debugShowCheckedModeBanner: false,
|
|
||||||
home: demoScreenRouter(_currentDemoScreen),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@js.JSExport()
|
|
||||||
void onTokenReceived(String token) {
|
|
||||||
if (token == 'token123') {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget demoScreenRouter(DemoScreen which) {
|
|
||||||
switch (which) {
|
|
||||||
case DemoScreen.counter:
|
|
||||||
return CounterDemo(
|
|
||||||
title: 'Counter',
|
|
||||||
numToDisplay: _counterScreenCount,
|
|
||||||
incrementHandler: increment,
|
|
||||||
isLoading: _isLoading);
|
|
||||||
case DemoScreen.textField:
|
|
||||||
return const TextFieldDemo(title: 'Nasdfs');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@js.JSExport()
|
|
||||||
void changeDemoScreenTo(String screenString) {
|
|
||||||
setState(() {
|
|
||||||
switch (screenString) {
|
|
||||||
case 'counter':
|
|
||||||
_currentDemoScreen = DemoScreen.counter;
|
|
||||||
break;
|
|
||||||
case 'textField':
|
|
||||||
_currentDemoScreen = DemoScreen.textField;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CounterDemo extends StatefulWidget {
|
|
||||||
final String title;
|
|
||||||
final int numToDisplay;
|
|
||||||
final VoidCallback incrementHandler;
|
|
||||||
final bool isLoading;
|
|
||||||
|
|
||||||
const CounterDemo(
|
|
||||||
{super.key,
|
|
||||||
required this.title,
|
|
||||||
required this.numToDisplay,
|
|
||||||
required this.incrementHandler,
|
|
||||||
required this.isLoading});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<CounterDemo> createState() => _CounterDemoState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CounterDemoState extends State<CounterDemo> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: widget.isLoading
|
|
||||||
? null
|
|
||||||
: AppBar(
|
|
||||||
title: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: TextField(
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: 'Search',
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
suffixIcon: const Icon(
|
|
||||||
Icons.search,
|
|
||||||
color: Colors.blue,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {},
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
padding: const EdgeInsets.all(0),
|
|
||||||
shape: const CircleBorder(
|
|
||||||
side: BorderSide(
|
|
||||||
color: Colors.blue,
|
|
||||||
width: 2,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
Icons.shopping_basket,
|
|
||||||
color: Colors.white,
|
|
||||||
size: 36,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {},
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
padding: const EdgeInsets.all(0),
|
|
||||||
shape: const CircleBorder(
|
|
||||||
side: BorderSide(
|
|
||||||
color: Colors.blue,
|
|
||||||
width: 2,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
Icons.history,
|
|
||||||
color: Colors.white,
|
|
||||||
size: 36,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
body: widget.isLoading
|
|
||||||
? const Center(child: CircularProgressIndicator())
|
|
||||||
: const Center(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: <Widget>[
|
|
||||||
Text(
|
|
||||||
'Здесь будут товары',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TextFieldDemo extends StatelessWidget {
|
|
||||||
const TextFieldDemo({super.key, required this.title});
|
|
||||||
final String title;
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: Text(title),
|
|
||||||
),
|
|
||||||
body: const Center(
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.all(14.0),
|
|
||||||
child: TextField(
|
|
||||||
maxLines: null,
|
|
||||||
decoration: InputDecoration(border: OutlineInputBorder()),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
13
lib/main_mobile.dart
Normal file
13
lib/main_mobile.dart
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gymlink_module_web/states/mobile.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
runApp(const MyApp());
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyApp extends StatefulWidget {
|
||||||
|
const MyApp({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MyApp> createState() => MyAppStateMobile();
|
||||||
|
}
|
||||||
288
lib/pages/basket.dart
Normal file
288
lib/pages/basket.dart
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gymlink_module_web/components/app_bar.dart';
|
||||||
|
import 'package:gymlink_module_web/components/basket_item_card.dart';
|
||||||
|
import 'package:gymlink_module_web/components/heading.dart';
|
||||||
|
import 'package:gymlink_module_web/pages/order_confirmation.dart';
|
||||||
|
import 'package:gymlink_module_web/tools/prefs.dart';
|
||||||
|
|
||||||
|
List<Map<String, dynamic>> cart = [
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
class BasketPage extends StatefulWidget {
|
||||||
|
const BasketPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<BasketPage> createState() => _BasketPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BasketPageState extends State<BasketPage> {
|
||||||
|
List<Map<String, dynamic>> cartItems = [];
|
||||||
|
int totalPrice = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
getCart().then((value) {
|
||||||
|
setState(() {
|
||||||
|
cartItems = value.map((element) {
|
||||||
|
final item = cart.firstWhere((e) => e['id'] == element['id']);
|
||||||
|
return {...item, 'count': element['count'] as int};
|
||||||
|
}).toList();
|
||||||
|
totalPrice = cartItems.fold(
|
||||||
|
0,
|
||||||
|
(sum, item) =>
|
||||||
|
sum + int.parse(item['price']) * item['count'] as int);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeItem(String id) async {
|
||||||
|
final item = cartItems.firstWhere((element) => element['id'] == id);
|
||||||
|
bool toDelete = false;
|
||||||
|
setState(() {
|
||||||
|
if (item['count'] > 1) {
|
||||||
|
item['count']--;
|
||||||
|
cartItems[cartItems.indexOf(item)]['count'] = item['count'];
|
||||||
|
} else {
|
||||||
|
toDelete = true;
|
||||||
|
}
|
||||||
|
totalPrice = cartItems.fold(0,
|
||||||
|
(sum, item) => sum + int.parse(item['price']) * item['count'] as int);
|
||||||
|
});
|
||||||
|
if (toDelete) {
|
||||||
|
await _deleteItemAlert(id, item['name']);
|
||||||
|
} else {
|
||||||
|
await removeItemFromCart(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addItem(String id) async {
|
||||||
|
setState(() {
|
||||||
|
final item = cartItems.firstWhere((element) => element['id'] == id,
|
||||||
|
orElse: () => {
|
||||||
|
...cart.firstWhere((element) => element['id'] == id),
|
||||||
|
'count': 0
|
||||||
|
});
|
||||||
|
item['count']++;
|
||||||
|
cartItems[cartItems.indexOf(item)]['count'] = item['count'];
|
||||||
|
totalPrice = cartItems.fold(0,
|
||||||
|
(sum, item) => sum + int.parse(item['price']) * item['count'] as int);
|
||||||
|
});
|
||||||
|
await addItemToCart(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _deleteItemAlert(String id, String name) async {
|
||||||
|
return showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: true,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text('Удаление из корзины'),
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: ListBody(
|
||||||
|
children: [
|
||||||
|
Text('Вы действительно хотите убрать "$name" из корзины?'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
child: const Text('Отмена'),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
child: const Text('Удалить'),
|
||||||
|
onPressed: () {
|
||||||
|
removeItemFromCart(id);
|
||||||
|
setState(() {
|
||||||
|
cartItems.removeWhere((element) => element['id'] == id);
|
||||||
|
});
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _clearCartAlert() async {
|
||||||
|
return showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: true,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text('Очистка корзины'),
|
||||||
|
content: const SingleChildScrollView(
|
||||||
|
child: ListBody(
|
||||||
|
children: [
|
||||||
|
Text('Вы действительно хотите очистить корзину?'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
child: const Text('Отмена'),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
child: const Text('Очистить'),
|
||||||
|
onPressed: () {
|
||||||
|
clearCart();
|
||||||
|
setState(() {
|
||||||
|
cartItems = [];
|
||||||
|
});
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: const GymLinkAppBar(),
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
const GymLinkHeader(title: "Корзина"),
|
||||||
|
cartItems.isEmpty
|
||||||
|
? Expanded(
|
||||||
|
child: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text('Корзина пуста',
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.all(Radius.circular(50)),
|
||||||
|
),
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
),
|
||||||
|
child: const Text('Вернуться назад'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: cartItems.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final item = cartItems[index];
|
||||||
|
return BasketItemCard(
|
||||||
|
name: item['name'],
|
||||||
|
price: item['price'],
|
||||||
|
id: item['id'],
|
||||||
|
image: Image(
|
||||||
|
image: AssetImage('assets/${item['image']}'),
|
||||||
|
width: 50,
|
||||||
|
),
|
||||||
|
onTapPlus: () => addItem(item['id'].toString()),
|
||||||
|
onTapMinus: () =>
|
||||||
|
removeItem(item['id'].toString()),
|
||||||
|
quantity: item['count'].toString(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.symmetric(
|
||||||
|
horizontal: 10, vertical: 10),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Итого: $totalPrice',
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () => Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
const OrderConfirmationPage(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.all(Radius.circular(50)),
|
||||||
|
),
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
),
|
||||||
|
child: const Text('Оформить заказ'),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _clearCartAlert,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.all(Radius.circular(50))),
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
),
|
||||||
|
child: const Text('Очистить корзину'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 50),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
175
lib/pages/detail.dart
Normal file
175
lib/pages/detail.dart
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gymlink_module_web/components/app_bar.dart';
|
||||||
|
import 'package:gymlink_module_web/components/heading.dart';
|
||||||
|
import 'package:gymlink_module_web/tools/prefs.dart';
|
||||||
|
|
||||||
|
//TODO: Сделать получение инфы через объект
|
||||||
|
class DetailPage extends StatefulWidget {
|
||||||
|
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
|
||||||
|
State<DetailPage> createState() => _DetailPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DetailPageState extends State<DetailPage> {
|
||||||
|
bool isInCart = false;
|
||||||
|
int quantity = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
getCart().then((value) {
|
||||||
|
setState(() {
|
||||||
|
isInCart = value.any((element) => element['id'] == widget.id);
|
||||||
|
if (isInCart) {
|
||||||
|
quantity = value
|
||||||
|
.firstWhere((element) => element['id'] == widget.id)['count'];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildButton() {
|
||||||
|
if (!isInCart) {
|
||||||
|
return ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await addItemToCart(widget.id);
|
||||||
|
setState(() {
|
||||||
|
isInCart = true;
|
||||||
|
quantity = 1;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
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('Добавить в корзину'),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.remove),
|
||||||
|
onPressed: () async {
|
||||||
|
await removeItemFromCart(widget.id);
|
||||||
|
setState(() {
|
||||||
|
if (quantity > 1) {
|
||||||
|
quantity--;
|
||||||
|
} else {
|
||||||
|
isInCart = false;
|
||||||
|
quantity = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Text('$quantity'),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
onPressed: () async {
|
||||||
|
await addItemToCart(widget.id);
|
||||||
|
setState(() {
|
||||||
|
quantity++;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: const GymLinkAppBar(),
|
||||||
|
body: Column(mainAxisAlignment: MainAxisAlignment.start, children: [
|
||||||
|
GymLinkHeader(title: '${widget.name} - ${widget.id}'),
|
||||||
|
Expanded(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
child: SizedBox(
|
||||||
|
width: MediaQuery.sizeOf(context).width,
|
||||||
|
height: MediaQuery.sizeOf(context).height,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
widget.image,
|
||||||
|
Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsetsDirectional.fromSTEB(0, 30, 60, 60),
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
minWidth: 340,
|
||||||
|
maxWidth: 340,
|
||||||
|
maxHeight: 600,
|
||||||
|
),
|
||||||
|
child: Card(
|
||||||
|
elevation: 4,
|
||||||
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.fromSTEB(
|
||||||
|
20, 15, 10, 15),
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
minHeight: 100,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
widget.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(
|
||||||
|
'Стоимость ${widget.price}',
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
|
),
|
||||||
|
_buildButton()
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
209
lib/pages/main.dart
Normal file
209
lib/pages/main.dart
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gymlink_module_web/components/app_bar.dart';
|
||||||
|
import 'package:gymlink_module_web/components/item_card.dart';
|
||||||
|
import 'package:gymlink_module_web/pages/basket.dart';
|
||||||
|
import 'package:gymlink_module_web/pages/detail.dart';
|
||||||
|
import 'package:gymlink_module_web/pages/order_history.dart';
|
||||||
|
import 'package:gymlink_module_web/tools/relative.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
const List<Map<String, String>> testData = [
|
||||||
|
{
|
||||||
|
"name": "Протеин",
|
||||||
|
"image": "product.png",
|
||||||
|
"price": "120",
|
||||||
|
"details": "Test details",
|
||||||
|
"id": "34fa3126-bfaf-5dec-8f4a-b246c097ef73"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Протеин",
|
||||||
|
"image": "product.png",
|
||||||
|
"price": "150",
|
||||||
|
"details":
|
||||||
|
"that name factory say string eaten order harbor easier said tone now floor nest it comfortable such difficulty labor bridge fact market women badly chamber heading forest allow shirt possibly story strip elephant extra even joy lungs than low discussion barn rapidly evidence is stream crew let more sold bag river triangle court slept knowledge flat package research balloon station underline careful market better make curious secret boy poor captured creature harder public tool ring subject charge planet tone scientist piece page stone support bush way feathers summer describe back should said complex song giant his that name factory say string eaten order harbor easier said tone now floor nest it comfortable such difficulty labor bridge fact market women badly chamber heading forest allow shirt possibly story strip elephant extra even joy lungs than low discussion barn rapidly evidence is stream crew let more sold bag river triangle court slept knowledge flat package research balloon station underline careful market better make curious secret boy poor captured creature harder public tool ring subject charge planet tone scientist piece page stone support bush way feathers summer describe back should said complex song giant his that name factory say string eaten order harbor easier said tone now floor nest it comfortable such difficulty labor bridge fact market women badly chamber heading forest allow shirt possibly story strip elephant extra even joy lungs than low discussion barn rapidly evidence is stream crew let more sold bag river triangle court slept knowledge flat package research balloon station underline careful market better make curious secret boy poor captured creature harder public tool ring subject charge planet tone scientist piece page stone support bush way feathers summer describe back should said complex song giant his that name factory say string eaten order harbor easier said tone now floor nest it comfortable such difficulty labor bridge fact market women badly chamber heading forest allow shirt possibly story strip elephant extra even joy lungs than low discussion barn rapidly evidence is stream crew let more sold bag river triangle court slept knowledge flat package research balloon station underline careful market better make curious secret boy poor captured creature harder public tool ring subject charge planet tone scientist piece page stone support bush way feathers summer describe back should said complex song giant his",
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
class MainPage extends StatefulWidget {
|
||||||
|
final bool isLoading;
|
||||||
|
|
||||||
|
const MainPage({
|
||||||
|
super.key,
|
||||||
|
required this.isLoading,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MainPage> createState() => _MainPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MainPageState extends State<MainPage> {
|
||||||
|
Future<void> _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(
|
||||||
|
appBar: widget.isLoading ? null : const GymLinkAppBar(),
|
||||||
|
body: widget.isLoading
|
||||||
|
? const Center(child: CircularProgressIndicator())
|
||||||
|
: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 5),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'Search',
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
suffixIcon: Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 8),
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: _goToPage,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 8,
|
||||||
|
horizontal: 0,
|
||||||
|
),
|
||||||
|
minimumSize:
|
||||||
|
const Size(50, kMinInteractiveDimension),
|
||||||
|
backgroundColor:
|
||||||
|
Theme.of(context).primaryColor,
|
||||||
|
shape: const CircleBorder(),
|
||||||
|
),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.search,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
getSpacer(context: context, flex: 2),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).push(MaterialPageRoute(
|
||||||
|
builder: (context) => const BasketPage(),
|
||||||
|
));
|
||||||
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.all(0),
|
||||||
|
minimumSize: const Size(50, kMinInteractiveDimension),
|
||||||
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
|
shape: const CircleBorder(
|
||||||
|
side: BorderSide(
|
||||||
|
color: Colors.black,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.shopping_basket,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).push(MaterialPageRoute(
|
||||||
|
builder: (context) => const HistoryPage(),
|
||||||
|
));
|
||||||
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.all(0),
|
||||||
|
minimumSize: const Size(50, kMinInteractiveDimension),
|
||||||
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
|
shape: const CircleBorder(
|
||||||
|
side: BorderSide(
|
||||||
|
color: Colors.black,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.history,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 10,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: GridView.builder(
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount:
|
||||||
|
(MediaQuery.sizeOf(context).width ~/ 250).floor(),
|
||||||
|
),
|
||||||
|
itemCount: testData.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final product = testData[index];
|
||||||
|
return ProductCard(
|
||||||
|
imagePath: Image(
|
||||||
|
image: AssetImage('assets/${product['image']!}'),
|
||||||
|
width: 100,
|
||||||
|
),
|
||||||
|
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(
|
||||||
|
image:
|
||||||
|
AssetImage('assets/${product['image']!}'),
|
||||||
|
width: 300),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
160
lib/pages/order_confirmation.dart
Normal file
160
lib/pages/order_confirmation.dart
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
|
import 'package:gymlink_module_web/components/app_bar.dart';
|
||||||
|
import 'package:gymlink_module_web/components/heading.dart';
|
||||||
|
import 'package:gymlink_module_web/components/order_confirm_item_card.dart';
|
||||||
|
import 'package:gymlink_module_web/tools/prefs.dart';
|
||||||
|
|
||||||
|
List<Map<String, dynamic>> cart = [
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
class OrderConfirmationPage extends StatefulWidget {
|
||||||
|
const OrderConfirmationPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<OrderConfirmationPage> createState() => _OrderConfirmationPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _OrderConfirmationPageState extends State<OrderConfirmationPage> {
|
||||||
|
List<Map<String, dynamic>> cartItems = [];
|
||||||
|
int totalPrice = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
getCart().then((value) {
|
||||||
|
setState(() {
|
||||||
|
cartItems = value.map((element) {
|
||||||
|
final item = cart.firstWhere((e) => e['id'] == element['id']);
|
||||||
|
return {...item, 'count': element['count'] as int};
|
||||||
|
}).toList();
|
||||||
|
totalPrice = cartItems.fold(
|
||||||
|
0,
|
||||||
|
(sum, item) =>
|
||||||
|
sum + int.parse(item['price']) * item['count'] as int);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: const GymLinkAppBar(),
|
||||||
|
body: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const GymLinkHeader(title: 'Оформление заказа'),
|
||||||
|
const MarkdownBody(data: '## Состав заказа:'),
|
||||||
|
Expanded(
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(maxHeight: 350),
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: cartItems.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final item = cartItems[index];
|
||||||
|
return OrderConfirmItemCard(
|
||||||
|
name: item['name'],
|
||||||
|
image: Image(
|
||||||
|
image: AssetImage('assets/${item['image']}'),
|
||||||
|
width: 50,
|
||||||
|
height: 50),
|
||||||
|
price: double.parse(item['price']),
|
||||||
|
count: item['count'],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
MarkdownBody(data: '## Итого: $totalPrice'),
|
||||||
|
Expanded(
|
||||||
|
child: TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'Адрес доставки',
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'Получатель',
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
print('debugprint');
|
||||||
|
// if (kIsWeb) {
|
||||||
|
// Navigator.of(context).push(
|
||||||
|
// MaterialPageRoute(
|
||||||
|
// builder: (context) => const OrderPayPage(),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// } else {
|
||||||
|
// debugPrint('test');
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(50)),
|
||||||
|
),
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
),
|
||||||
|
child: const Text('Оформить заказ'),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
77
lib/pages/order_history.dart
Normal file
77
lib/pages/order_history.dart
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gymlink_module_web/components/app_bar.dart';
|
||||||
|
import 'package:gymlink_module_web/components/heading.dart';
|
||||||
|
import 'package:gymlink_module_web/components/history_item_card.dart';
|
||||||
|
import 'package:gymlink_module_web/tools/relative.dart';
|
||||||
|
|
||||||
|
List<Map<String, String>> orders = [
|
||||||
|
{"image": "product.png", "price": "120", "id": "66", "date": "11.09.2001"},
|
||||||
|
{
|
||||||
|
"image": "product.png",
|
||||||
|
"price": "150",
|
||||||
|
"id": "56",
|
||||||
|
"date": "11.09.2001",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "product.png",
|
||||||
|
"price": "250",
|
||||||
|
"id": "98",
|
||||||
|
"date": "11.09.2001",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "product.png",
|
||||||
|
"price": "300",
|
||||||
|
"id": "50",
|
||||||
|
"date": "11.09.2001",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "product.png",
|
||||||
|
"price": "100",
|
||||||
|
"id": "30",
|
||||||
|
"date": "11.09.2001",
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
class HistoryPage extends StatelessWidget {
|
||||||
|
const HistoryPage({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: const GymLinkAppBar(),
|
||||||
|
body: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const GymLinkHeader(title: 'История заказов'),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: orders.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final item = orders[index];
|
||||||
|
return HistoryItemCard(
|
||||||
|
id: item['id']!,
|
||||||
|
cost: item['price']!,
|
||||||
|
date: item['date']!,
|
||||||
|
image: Image(
|
||||||
|
image: AssetImage('assets/${item['image']!}'),
|
||||||
|
width: 50,
|
||||||
|
),
|
||||||
|
status: OrderStatus.completed,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
getSpacer(context: context)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
26
lib/pages/order_pay.dart
Normal file
26
lib/pages/order_pay.dart
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import 'dart:ui_web' as ui;
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:universal_html/html.dart';
|
||||||
|
|
||||||
|
class OrderPayPage extends StatelessWidget {
|
||||||
|
const OrderPayPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
registerHtmlElementView();
|
||||||
|
return const HtmlElementView(
|
||||||
|
viewType: 'payment-html',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerHtmlElementView() {
|
||||||
|
ui.platformViewRegistry.registerViewFactory(
|
||||||
|
'payment-html',
|
||||||
|
(int viewId) => IFrameElement()
|
||||||
|
..src = 'payment.html'
|
||||||
|
..style.width = '100%'
|
||||||
|
..style.height = '500px'
|
||||||
|
..style.border = 'none');
|
||||||
|
}
|
||||||
|
}
|
||||||
35
lib/states/mobile.dart
Normal file
35
lib/states/mobile.dart
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gymlink_module_web/main_mobile.dart';
|
||||||
|
import 'package:gymlink_module_web/pages/main.dart';
|
||||||
|
import 'package:gymlink_module_web/theme.dart';
|
||||||
|
|
||||||
|
class MyAppStateMobile extends State<MyApp> {
|
||||||
|
bool _isLoading = false;
|
||||||
|
ThemeData theme = myTheme;
|
||||||
|
bool black_theme = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MaterialApp(
|
||||||
|
title: 'GymLink Module',
|
||||||
|
theme: theme,
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
|
home: MainPage(isLoading: _isLoading),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onTokenReceived(String token) {
|
||||||
|
if (token == 'token123') {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void changeColor(int color) {
|
||||||
|
setState(() {
|
||||||
|
black_theme = !black_theme; //FIXME: TEMPORARY
|
||||||
|
theme = getThemeData(Color(color), black_theme);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
57
lib/states/web.dart
Normal file
57
lib/states/web.dart
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:js_interop' as js;
|
||||||
|
import 'dart:js_interop_unsafe' as js_util;
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gymlink_module_web/main.dart';
|
||||||
|
import 'package:gymlink_module_web/pages/main.dart';
|
||||||
|
import 'package:gymlink_module_web/theme.dart';
|
||||||
|
|
||||||
|
@js.JSExport()
|
||||||
|
class MyAppStateWeb extends State<MyApp> {
|
||||||
|
final _streamController = StreamController<void>.broadcast();
|
||||||
|
bool _isLoading = true;
|
||||||
|
ThemeData theme = myTheme;
|
||||||
|
bool black_theme = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
final export = js.createJSInteropWrapper(this);
|
||||||
|
js.globalContext['_appState'] = export;
|
||||||
|
js.globalContext.callMethod('_stateSet'.toJS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_streamController.close();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MaterialApp(
|
||||||
|
title: 'GymLink Module',
|
||||||
|
theme: theme,
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
|
home: MainPage(isLoading: _isLoading),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@js.JSExport()
|
||||||
|
void onTokenReceived(String token) {
|
||||||
|
if (token == 'token123') {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@js.JSExport()
|
||||||
|
void changeColor(int color) {
|
||||||
|
setState(() {
|
||||||
|
black_theme = !black_theme; //FIXME: TEMPORARY
|
||||||
|
theme = getThemeData(Color(color), black_theme);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
39
lib/theme.dart
Normal file
39
lib/theme.dart
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
final ThemeData myTheme = ThemeData(
|
||||||
|
colorScheme: ColorScheme.fromSeed(
|
||||||
|
seedColor: getMaterialColor(const Color(0x007d85ff))));
|
||||||
|
|
||||||
|
ThemeData getThemeData(Color color, bool dark) {
|
||||||
|
final MaterialColor materialColor = getMaterialColor(color);
|
||||||
|
return ThemeData(
|
||||||
|
colorScheme: ColorScheme.fromSeed(
|
||||||
|
seedColor: materialColor,
|
||||||
|
brightness: dark ? Brightness.dark : Brightness.light,
|
||||||
|
).copyWith(
|
||||||
|
onPrimary: dark ? materialColor[600] : Colors.white,
|
||||||
|
),
|
||||||
|
useMaterial3: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialColor getMaterialColor(Color color) {
|
||||||
|
final int red = color.red;
|
||||||
|
final int green = color.green;
|
||||||
|
final int blue = color.blue;
|
||||||
|
|
||||||
|
final Map<int, Color> shades = {
|
||||||
|
50: Color.fromRGBO(red, green, blue, .1),
|
||||||
|
100: Color.fromRGBO(red, green, blue, .2),
|
||||||
|
200: Color.fromRGBO(red, green, blue, .3),
|
||||||
|
300: Color.fromRGBO(red, green, blue, .4),
|
||||||
|
400: Color.fromRGBO(red, green, blue, .5),
|
||||||
|
500: Color.fromRGBO(red, green, blue, .6),
|
||||||
|
600: Color.fromRGBO(red, green, blue, .7),
|
||||||
|
700: Color.fromRGBO(red, green, blue, .8),
|
||||||
|
800: Color.fromRGBO(red, green, blue, .9),
|
||||||
|
900: Color.fromRGBO(red, green, blue, 1),
|
||||||
|
};
|
||||||
|
|
||||||
|
return MaterialColor(color.value, shades);
|
||||||
|
}
|
||||||
47
lib/tools/prefs.dart
Normal file
47
lib/tools/prefs.dart
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
Future<void> addItemToCart(String id) async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
String cartString = prefs.getString('cart') ?? "[]";
|
||||||
|
List<Map<String, dynamic>> cart =
|
||||||
|
List<Map<String, dynamic>>.from(jsonDecode(cartString) as List<dynamic>);
|
||||||
|
final index = cart.indexWhere((element) => element['id'] == id);
|
||||||
|
if (index == -1) {
|
||||||
|
cart.add({'id': id, 'count': 1});
|
||||||
|
} else {
|
||||||
|
cart[index]['count'] = cart[index]['count']! + 1;
|
||||||
|
}
|
||||||
|
prefs.setString('cart', jsonEncode(cart));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Map<String, dynamic>>> getCart() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
String cartString = prefs.getString('cart') ?? "[]";
|
||||||
|
List<Map<String, dynamic>> cart =
|
||||||
|
List<Map<String, dynamic>>.from(jsonDecode(cartString) as List<dynamic>);
|
||||||
|
return cart;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> removeItemFromCart(String id) async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
String cartString = prefs.getString('cart') ?? "[]";
|
||||||
|
List<Map<String, dynamic>> cart =
|
||||||
|
List<Map<String, dynamic>>.from(jsonDecode(cartString) as List<dynamic>);
|
||||||
|
cart.removeWhere((element) => element['id'] == id && element['count'] == 1);
|
||||||
|
for (final item in cart) {
|
||||||
|
if (item['id'] == id) {
|
||||||
|
item['count'] = item['count']! - 1;
|
||||||
|
if (item['count'] == 0) {
|
||||||
|
cart.remove(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prefs.setString('cart', jsonEncode(cart));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> clearCart() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
prefs.setString('cart', "[]");
|
||||||
|
}
|
||||||
11
lib/tools/relative.dart
Normal file
11
lib/tools/relative.dart
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
Widget getSpacer(
|
||||||
|
{required BuildContext context, int flex = 1, double width = 10}) {
|
||||||
|
if (MediaQuery.of(context).size.width > 600) {
|
||||||
|
return Spacer(
|
||||||
|
flex: flex,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return SizedBox(width: width);
|
||||||
|
}
|
||||||
@@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
url_launcher_linux
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
import shared_preferences_foundation
|
||||||
|
import url_launcher_macos
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
294
pubspec.lock
294
pubspec.lock
@@ -1,6 +1,14 @@
|
|||||||
# Generated by pub
|
# Generated by pub
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
packages:
|
packages:
|
||||||
|
args:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: args
|
||||||
|
sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.5.0"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -25,6 +33,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
|
charcode:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: charcode
|
||||||
|
sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.1"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -41,6 +57,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.18.0"
|
version: "1.18.0"
|
||||||
|
csslib:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: csslib
|
||||||
|
sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -57,6 +81,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.3.1"
|
||||||
|
ffi:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ffi
|
||||||
|
sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
|
file:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file
|
||||||
|
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.0"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -70,11 +110,48 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.2"
|
||||||
|
flutter_markdown:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_markdown
|
||||||
|
sha256: "9921f9deda326f8a885e202b1e35237eadfc1345239a0f6f0f1ff287e047547f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.1"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_web_plugins:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
html:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: html
|
||||||
|
sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.15.4"
|
||||||
|
http:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: http
|
||||||
|
sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
|
http_parser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_parser
|
||||||
|
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.2"
|
||||||
leak_tracker:
|
leak_tracker:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -107,6 +184,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0"
|
version: "3.0.0"
|
||||||
|
markdown:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: markdown
|
||||||
|
sha256: ef2a1298144e3f985cc736b22e0ccdaf188b5b3970648f2d9dc13efd1d9df051
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.2.2"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -139,6 +224,102 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.0"
|
version: "1.9.0"
|
||||||
|
path_provider_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_linux
|
||||||
|
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.1"
|
||||||
|
path_provider_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_platform_interface
|
||||||
|
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
|
path_provider_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_windows
|
||||||
|
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.1"
|
||||||
|
platform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: platform
|
||||||
|
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.4"
|
||||||
|
plugin_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: plugin_platform_interface
|
||||||
|
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.8"
|
||||||
|
shared_preferences:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: shared_preferences
|
||||||
|
sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.3"
|
||||||
|
shared_preferences_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_android
|
||||||
|
sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.2"
|
||||||
|
shared_preferences_foundation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_foundation
|
||||||
|
sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.5"
|
||||||
|
shared_preferences_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_linux
|
||||||
|
sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
|
shared_preferences_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_platform_interface
|
||||||
|
sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
|
shared_preferences_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_web
|
||||||
|
sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.0"
|
||||||
|
shared_preferences_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_windows
|
||||||
|
sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -192,6 +373,94 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.1"
|
version: "0.6.1"
|
||||||
|
typed_data:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: typed_data
|
||||||
|
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.2"
|
||||||
|
universal_html:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: universal_html
|
||||||
|
sha256: "56536254004e24d9d8cfdb7dbbf09b74cf8df96729f38a2f5c238163e3d58971"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.4"
|
||||||
|
universal_io:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: universal_io
|
||||||
|
sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.2"
|
||||||
|
url_launcher:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: url_launcher
|
||||||
|
sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.2.6"
|
||||||
|
url_launcher_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_android
|
||||||
|
sha256: "360a6ed2027f18b73c8d98e159dda67a61b7f2e0f6ec26e86c3ada33b0621775"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.3.1"
|
||||||
|
url_launcher_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_ios
|
||||||
|
sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.2.5"
|
||||||
|
url_launcher_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_linux
|
||||||
|
sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.1"
|
||||||
|
url_launcher_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_macos
|
||||||
|
sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.0"
|
||||||
|
url_launcher_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_platform_interface
|
||||||
|
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
|
url_launcher_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_web
|
||||||
|
sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.1"
|
||||||
|
url_launcher_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_windows
|
||||||
|
sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.1"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -208,5 +477,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "13.0.0"
|
version: "13.0.0"
|
||||||
|
web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: web
|
||||||
|
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.1"
|
||||||
|
win32:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: win32
|
||||||
|
sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.5.0"
|
||||||
|
xdg_directories:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xdg_directories
|
||||||
|
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.3.3 <4.0.0"
|
dart: ">=3.3.3 <4.0.0"
|
||||||
|
flutter: ">=3.19.0"
|
||||||
|
|||||||
12
pubspec.yaml
12
pubspec.yaml
@@ -1,5 +1,5 @@
|
|||||||
name: flutter_application_1
|
name: gymlink_module_web
|
||||||
description: "A new Flutter project."
|
description: "GymLink Flutter Web Module."
|
||||||
# The following line prevents the package from being accidentally published to
|
# The following line prevents the package from being accidentally published to
|
||||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
# 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
|
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
@@ -35,6 +35,11 @@ dependencies:
|
|||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.6
|
cupertino_icons: ^1.0.6
|
||||||
|
url_launcher: ^6.2.6
|
||||||
|
shared_preferences: ^2.2.3
|
||||||
|
flutter_markdown: ^0.7.1
|
||||||
|
http: ^1.2.1
|
||||||
|
universal_html: ^2.2.4
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@@ -57,6 +62,9 @@ flutter:
|
|||||||
# included with your application, so that you can use the icons in
|
# included with your application, so that you can use the icons in
|
||||||
# the material Icons class.
|
# the material Icons class.
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
assets:
|
||||||
|
- assets/logo.png
|
||||||
|
- assets/product.png
|
||||||
|
|
||||||
# To add assets to your application, add an assets section, like this:
|
# To add assets to your application, add an assets section, like this:
|
||||||
# assets:
|
# assets:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#flutter_target {
|
#flutter_target {
|
||||||
border: 1px solid #aaa;
|
border: 1px solid #aaa;
|
||||||
width: 320px;
|
width: 80vw;
|
||||||
height: 480px;
|
height: 60vh;
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
transition: all 150ms ease-in;
|
transition: all 150ms ease-in;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
|
|||||||
@@ -42,6 +42,8 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<button id='token'>Token btn</button>
|
<button id='token'>Token btn</button>
|
||||||
|
<button id='colorChangeBtnRed'>Color btn Red</button>
|
||||||
|
<button id='colorChangeBtnBlue'>Color btn Blue</button>
|
||||||
<section class='contents'>
|
<section class='contents'>
|
||||||
<article>
|
<article>
|
||||||
<div id="flutter_target"></div>
|
<div id="flutter_target"></div>
|
||||||
|
|||||||
@@ -12,5 +12,20 @@
|
|||||||
appState.onTokenReceived('token123');
|
appState.onTokenReceived('token123');
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let colorChangeBtnRed = document.getElementById('colorChangeBtnRed');
|
||||||
|
colorChangeBtnRed.addEventListener('click', function() {
|
||||||
|
var hexColor = '#FF0000'.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i
|
||||||
|
, (m, r, g, b) => '#ff' + r + r + g + g + b + b).substring(1);
|
||||||
|
var numColor = parseInt(hexColor, 16);
|
||||||
|
appState.changeColor(numColor)
|
||||||
|
})
|
||||||
|
|
||||||
|
let colorChangeBtnBlue = document.getElementById('colorChangeBtnBlue');
|
||||||
|
colorChangeBtnBlue.addEventListener('click', function() {
|
||||||
|
var hexColor = '#0000FF'.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i
|
||||||
|
, (m, r, g, b) => '#ff' + r + r + g + g + b + b).substring(1);
|
||||||
|
var numColor = parseInt(hexColor, 16);
|
||||||
|
appState.changeColor(numColor)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}());
|
}());
|
||||||
51
web/payment.html
Normal file
51
web/payment.html
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<title>Прием платежа с помощью виджета ЮKassa</title>
|
||||||
|
|
||||||
|
<!--Подключение библиотеки для инициализации виджета ЮKassa-->
|
||||||
|
<script src="https://yookassa.ru/checkout-widget/v1/checkout-widget.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
Ниже отобразится платежная форма. Если вы еще не создавали платеж и не передавали токен для инициализации виджета, появится сообщение об ошибке.
|
||||||
|
|
||||||
|
<!--Контейнер, в котором будет отображаться платежная форма-->
|
||||||
|
<div id="payment-form"></div>
|
||||||
|
|
||||||
|
Данные банковской карты для оплаты в <b>тестовом магазине</b>:
|
||||||
|
|
||||||
|
- номер — <b>5555 5555 5555 4477</b>
|
||||||
|
- срок действия — <b>01/30</b> (или другая дата, больше текущей)
|
||||||
|
- CVC — <b>123</b> (или три любые цифры)
|
||||||
|
- код для прохождения 3-D Secure — <b>123</b> (или три любые цифры)
|
||||||
|
|
||||||
|
<a href=https://yookassa.ru/developers/payment-acceptance/testing-and-going-live/testing#test-bank-card>Другие тестовые банковские карты</a>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
//Инициализация виджета. Все параметры обязательные.
|
||||||
|
const checkout = new window.YooMoneyCheckoutWidget({
|
||||||
|
confirmation_token: 'ct-287e0c37-000f-5000-8000-16961d35b0fd', //Токен, который перед проведением оплаты нужно получить от ЮKassa
|
||||||
|
return_url: 'https://example.com/', //Ссылка на страницу завершения оплаты, это может быть любая ваша страница
|
||||||
|
|
||||||
|
//При необходимости можно изменить цвета виджета, подробные настройки см. в документации
|
||||||
|
//customization: {
|
||||||
|
//Настройка цветовой схемы, минимум один параметр, значения цветов в HEX
|
||||||
|
//colors: {
|
||||||
|
//Цвет акцентных элементов: кнопка Заплатить, выбранные переключатели, опции и текстовые поля
|
||||||
|
//control_primary: '#00BF96', //Значение цвета в HEX
|
||||||
|
|
||||||
|
//Цвет платежной формы и ее элементов
|
||||||
|
//background: '#F2F3F5' //Значение цвета в HEX
|
||||||
|
//}
|
||||||
|
//},
|
||||||
|
error_callback: function(error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//Отображение платежной формы в контейнере
|
||||||
|
checkout.render('payment-form');
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -6,6 +6,9 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
url_launcher_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|||||||
Reference in New Issue
Block a user