Compare commits
7 Commits
master
...
ff29598ec5
| Author | SHA1 | Date | |
|---|---|---|---|
| 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
|
||||
}
|
||||
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 |
36
lib/components/app_bar.dart
Normal file
36
lib/components/app_bar.dart
Normal file
@@ -0,0 +1,36 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class GymLinkAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
const GymLinkAppBar({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
shadowColor: null,
|
||||
automaticallyImplyLeading: false,
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 4,
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8),
|
||||
child: Image.asset('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);
|
||||
}
|
||||
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),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
44
lib/components/item_card.dart
Normal file
44
lib/components/item_card.dart
Normal file
@@ -0,0 +1,44 @@
|
||||
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,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(minHeight: 200),
|
||||
child: Card(
|
||||
elevation: 3,
|
||||
color: const Color(0xFFF2F3F9),
|
||||
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),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
259
lib/main.dart
259
lib/main.dart
@@ -3,13 +3,18 @@ import 'dart:js_interop' as js;
|
||||
import 'dart:js_interop_unsafe' as js_util;
|
||||
|
||||
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/theme.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
void main() {
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
enum DemoScreen { counter, textField }
|
||||
|
||||
class MyApp extends StatefulWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
@@ -18,11 +23,47 @@ class MyApp extends StatefulWidget {
|
||||
State<MyApp> createState() => _MyAppState();
|
||||
}
|
||||
|
||||
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": "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<MyApp> {
|
||||
final _streanController = StreamController<void>.broadcast();
|
||||
DemoScreen _currentDemoScreen = DemoScreen.counter;
|
||||
int _counterScreenCount = 0;
|
||||
final _streamController = StreamController<void>.broadcast();
|
||||
bool _isLoading = true;
|
||||
|
||||
@override
|
||||
@@ -35,39 +76,17 @@ class _MyAppState extends State<MyApp> {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_streanController.close();
|
||||
_streamController.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),
|
||||
),
|
||||
title: 'GymLink Module',
|
||||
theme: myTheme,
|
||||
debugShowCheckedModeBanner: false,
|
||||
home: demoScreenRouter(_currentDemoScreen),
|
||||
home: MainPage(isLoading: _isLoading),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -79,60 +98,41 @@ class _MyAppState extends State<MyApp> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
class MainPage extends StatefulWidget {
|
||||
final bool isLoading;
|
||||
|
||||
const CounterDemo(
|
||||
{super.key,
|
||||
required this.title,
|
||||
required this.numToDisplay,
|
||||
required this.incrementHandler,
|
||||
required this.isLoading});
|
||||
const MainPage({
|
||||
super.key,
|
||||
required this.isLoading,
|
||||
});
|
||||
|
||||
@override
|
||||
State<CounterDemo> createState() => _CounterDemoState();
|
||||
State<MainPage> createState() => _MainPageState();
|
||||
}
|
||||
|
||||
class _CounterDemoState extends State<CounterDemo> {
|
||||
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
|
||||
: AppBar(
|
||||
title: Row(
|
||||
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(
|
||||
@@ -141,86 +141,115 @@ class _CounterDemoState extends State<CounterDemo> {
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
suffixIcon: const Icon(
|
||||
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.blue,
|
||||
color: Colors.white,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ElevatedButton(
|
||||
onPressed: () {},
|
||||
onPressed: () {
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) => const BasketPage(),
|
||||
));
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.all(0),
|
||||
minimumSize: const Size(40, kMinInteractiveDimension),
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
shape: const CircleBorder(
|
||||
side: BorderSide(
|
||||
color: Colors.blue,
|
||||
width: 2,
|
||||
color: Colors.black,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.shopping_basket,
|
||||
color: Colors.white,
|
||||
size: 36,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ElevatedButton(
|
||||
onPressed: () {},
|
||||
onPressed: () {
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) => const HistoryPage(),
|
||||
));
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.all(0),
|
||||
minimumSize: const Size(40, kMinInteractiveDimension),
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
shape: const CircleBorder(
|
||||
side: BorderSide(
|
||||
color: Colors.blue,
|
||||
width: 2,
|
||||
color: Colors.black,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.history,
|
||||
color: Colors.white,
|
||||
size: 36,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: widget.isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: const Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'Здесь будут товары',
|
||||
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.asset(
|
||||
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.asset(product['image']!, width: 300),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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()),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
28
lib/pages/basket.dart
Normal file
28
lib/pages/basket.dart
Normal file
@@ -0,0 +1,28 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gymlink_module_web/components/app_bar.dart';
|
||||
import 'package:gymlink_module_web/components/heading.dart';
|
||||
|
||||
//TODO: Вывод корзины из shared_prefs
|
||||
class BasketPage extends StatefulWidget {
|
||||
const BasketPage({super.key});
|
||||
|
||||
@override
|
||||
State<BasketPage> createState() => _BasketPageState();
|
||||
}
|
||||
|
||||
class _BasketPageState extends State<BasketPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Scaffold(
|
||||
appBar: GymLinkAppBar(),
|
||||
body: Column(
|
||||
children: [
|
||||
GymLinkHeader(title: 'Корзина'),
|
||||
Center(
|
||||
child: Text('Корзина'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
163
lib/pages/detail.dart
Normal file
163
lib/pages/detail.dart
Normal file
@@ -0,0 +1,163 @@
|
||||
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) {
|
||||
debugPrint(value.toString());
|
||||
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;
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
Text('$quantity'),
|
||||
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,
|
||||
children: [
|
||||
widget.image,
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsetsDirectional.fromSTEB(0, 30, 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(
|
||||
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()
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
21
lib/pages/order_history.dart
Normal file
21
lib/pages/order_history.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gymlink_module_web/components/app_bar.dart';
|
||||
import 'package:gymlink_module_web/components/heading.dart';
|
||||
|
||||
class HistoryPage extends StatelessWidget {
|
||||
const HistoryPage({
|
||||
super.key,
|
||||
});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Scaffold(
|
||||
appBar: GymLinkAppBar(),
|
||||
body: Column(mainAxisAlignment: MainAxisAlignment.start, children: [
|
||||
GymLinkHeader(title: 'История заказов'),
|
||||
Center(
|
||||
child: Text('История заказов'),
|
||||
)
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
26
lib/theme.dart
Normal file
26
lib/theme.dart
Normal file
@@ -0,0 +1,26 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
final ThemeData myTheme = ThemeData(
|
||||
colorScheme: ColorScheme.fromSeed(
|
||||
seedColor: getMaterialColor(const Color(0x007d85ff))));
|
||||
|
||||
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);
|
||||
}
|
||||
42
lib/tools/prefs.dart
Normal file
42
lib/tools/prefs.dart
Normal file
@@ -0,0 +1,42 @@
|
||||
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));
|
||||
}
|
||||
@@ -6,6 +6,10 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
|
||||
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
|
||||
url_launcher_linux
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import shared_preferences_foundation
|
||||
import url_launcher_macos
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
}
|
||||
|
||||
206
pubspec.lock
206
pubspec.lock
@@ -57,6 +57,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
@@ -75,6 +91,11 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -139,6 +160,102 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
@@ -192,6 +309,70 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.1"
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -208,5 +389,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dart: ">=3.3.3 <4.0.0"
|
||||
flutter: ">=3.19.0"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: flutter_application_1
|
||||
description: "A new Flutter project."
|
||||
name: gymlink_module_web
|
||||
description: "GymLink Flutter Web Module."
|
||||
# The following line prevents the package from being accidentally published to
|
||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
@@ -35,6 +35,8 @@ 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
|
||||
shared_preferences: ^2.2.3
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
@@ -57,6 +59,9 @@ flutter:
|
||||
# included with your application, so that you can use the icons in
|
||||
# the material Icons class.
|
||||
uses-material-design: true
|
||||
assets:
|
||||
- assets/logo.png
|
||||
- assets/product.png
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
# assets:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#flutter_target {
|
||||
border: 1px solid #aaa;
|
||||
width: 320px;
|
||||
height: 480px;
|
||||
width: 80vw;
|
||||
height: 60vh;
|
||||
border-radius: 0px;
|
||||
transition: all 150ms ease-in;
|
||||
align-self: center;
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
UrlLauncherWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
url_launcher_windows
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
||||
Reference in New Issue
Block a user