From 986a9d9bd5e6ac8da3c5b48b3acc812869ff726c Mon Sep 17 00:00:00 2001 From: Sergey Elpashev Date: Fri, 17 May 2024 16:11:05 +0300 Subject: [PATCH] Error provider --- lib/main.dart | 14 +- lib/pages/basket.dart | 2 + lib/pages/main.dart | 315 ++++++++++++++++++-------------------- lib/providers/main.dart | 11 ++ lib/states/mobile.dart | 14 +- lib/states/web.dart | 34 ++-- web/js/demo-js-interop.js | 52 ++++--- 7 files changed, 238 insertions(+), 204 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index f4eed45..6d19001 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:gymlink_module_web/providers/main.dart'; import 'package:gymlink_module_web/states/web.dart'; +import 'package:provider/provider.dart'; void main() { - runApp(const MyApp()); + runApp(const MyAppWithProvider()); } class MyApp extends StatefulWidget { @@ -11,3 +13,13 @@ class MyApp extends StatefulWidget { @override State createState() => MyAppStateWeb(); } + +class MyAppWithProvider extends StatelessWidget { + const MyAppWithProvider({super.key}); + + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider( + create: (_) => GymLinkProvider(), child: const MyApp()); + } +} diff --git a/lib/pages/basket.dart b/lib/pages/basket.dart index b2b0a99..de3240f 100644 --- a/lib/pages/basket.dart +++ b/lib/pages/basket.dart @@ -4,6 +4,7 @@ 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/providers/cart.dart'; +import 'package:gymlink_module_web/providers/main.dart'; import 'package:gymlink_module_web/tools/prefs.dart'; import 'package:gymlink_module_web/tools/routes.dart'; import 'package:lazy_load_scrollview/lazy_load_scrollview.dart'; @@ -77,6 +78,7 @@ class _BasketPageState extends State { void _updateCart() { Provider.of(context, listen: false).updateCartLength(); + Provider.of(context, listen: false).onTokenReceived('123'); } void removeItem(String id) async { diff --git a/lib/pages/main.dart b/lib/pages/main.dart index 0aecf6a..a4af639 100644 --- a/lib/pages/main.dart +++ b/lib/pages/main.dart @@ -7,6 +7,7 @@ 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/providers/cart.dart'; +import 'package:gymlink_module_web/providers/main.dart'; import 'package:gymlink_module_web/tools/prefs.dart'; import 'package:gymlink_module_web/tools/relative.dart'; import 'package:gymlink_module_web/tools/routes.dart'; @@ -54,11 +55,8 @@ const List> testData = [ ]; class MainPage extends StatefulWidget { - final bool isLoading; - const MainPage({ super.key, - required this.isLoading, }); @override @@ -108,177 +106,170 @@ class _MainPageState extends State { @override Widget build(BuildContext context) { final cartL = context.watch().cartLength; + final onError = context.read().onError; return Scaffold( - appBar: widget.isLoading ? null : const GymLinkAppBar(), - body: widget.isLoading - ? const Center(child: CircularProgressIndicator()) - : Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 5), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Expanded( - child: TextField( - onChanged: (value) => setState(() { - searchText = value; - }), - decoration: InputDecoration( - hintText: 'Поиск', - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - ), - suffixIcon: Padding( - padding: const EdgeInsets.only(right: 8), - child: ElevatedButton( - onPressed: _onSearch, - 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), - const SizedBox( - width: 8, - ), - ElevatedButton( - onPressed: () { - Navigator.of(context).push(CustomPageRoute( - 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, - ) - ], - ), - ), + appBar: const GymLinkAppBar(), + body: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 5), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ Expanded( - child: LazyLoadScrollView( - onEndOfPage: _onLoad, - child: Stack( - children: [ - GridView.builder( - gridDelegate: - SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: min( - (MediaQuery.sizeOf(context).width ~/ 200) - .toInt(), - 8), + child: TextField( + onChanged: (value) => setState(() { + searchText = value; + }), + decoration: InputDecoration( + hintText: 'Поиск', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + ), + suffixIcon: Padding( + padding: const EdgeInsets.only(right: 8), + child: ElevatedButton( + onPressed: _onSearch, + 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, ), - itemCount: filteredData.length, - itemBuilder: (context, index) { - final product = filteredData[index]; - return ProductCard( - imagePath: Image( - image: Image.network( - 'https://rus-sport.net/upload/iblock/311/topb85ez18pq0aavohpa5zipk2sbfxll.jpg') - .image, - width: 50, - ), - name: product['name']!, - price: product['price']!, - onTap: () => Navigator.of(context).push( - CustomPageRoute( - builder: (context) => DetailPage( - name: product['name']!, - description: product['details']!, - price: product['price']!, - id: product['id']!, - image: Image( - image: AssetImage( - 'assets/${product['image']!}'), - width: 300), - ), - ), - ), - ); - }, ), - ], + ), ), ), ), + getSpacer(context: context, flex: 2), + const SizedBox( + width: 8, + ), + ElevatedButton( + onPressed: () { + onError(); + Navigator.of(context).push(CustomPageRoute( + 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, + ) ], ), - floatingActionButton: !widget.isLoading - ? SizedBox( - height: 80, - width: 80, - child: FittedBox( - child: Stack( - children: [ - FloatingActionButton( - onPressed: () => - Navigator.of(context).push(CustomPageRoute( - builder: (context) => const BasketPage(), - )), - backgroundColor: Colors.transparent, - elevation: 0, - child: CircleAvatar( - radius: 25, - backgroundColor: Theme.of(context).primaryColor, - foregroundColor: Colors.white, - child: const Icon(Icons.shopping_cart_outlined)), + ), + Expanded( + child: LazyLoadScrollView( + onEndOfPage: _onLoad, + child: Stack( + children: [ + GridView.builder( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: min( + (MediaQuery.sizeOf(context).width ~/ 200).toInt(), 8), ), - cartL > 0 - ? Positioned( - right: -3, - bottom: 0, - child: Card( - color: Colors.red, - child: SizedBox( - width: 20, - child: Center( - child: Text( - cartL.toString(), - style: const TextStyle(color: Colors.white), - ), - ), - ), + itemCount: filteredData.length, + itemBuilder: (context, index) { + final product = filteredData[index]; + return ProductCard( + imagePath: Image( + image: Image.network( + 'https://rus-sport.net/upload/iblock/311/topb85ez18pq0aavohpa5zipk2sbfxll.jpg') + .image, + width: 50, + ), + name: product['name']!, + price: product['price']!, + onTap: () => Navigator.of(context).push( + CustomPageRoute( + builder: (context) => DetailPage( + name: product['name']!, + description: product['details']!, + price: product['price']!, + id: product['id']!, + image: Image( + image: + AssetImage('assets/${product['image']!}'), + width: 300), ), - ) - : const SizedBox.shrink(), - ], - ), + ), + ), + ); + }, + ), + ], ), - ) - : null, + ), + ), + ], + ), + floatingActionButton: SizedBox( + height: 80, + width: 80, + child: FittedBox( + child: Stack( + children: [ + FloatingActionButton( + onPressed: () => Navigator.of(context).push(CustomPageRoute( + builder: (context) => const BasketPage(), + )), + backgroundColor: Colors.transparent, + elevation: 0, + child: CircleAvatar( + radius: 25, + backgroundColor: Theme.of(context).primaryColor, + foregroundColor: Colors.white, + child: const Icon(Icons.shopping_cart_outlined)), + ), + cartL > 0 + ? Positioned( + right: -3, + bottom: 0, + child: Card( + color: Colors.red, + child: SizedBox( + width: 20, + child: Center( + child: Text( + cartL.toString(), + style: const TextStyle(color: Colors.white), + ), + ), + ), + ), + ) + : const SizedBox.shrink(), + ], + ), + ), + ), floatingActionButtonLocation: FloatingActionButtonLocation.startFloat, ); } diff --git a/lib/providers/main.dart b/lib/providers/main.dart index 094b8fd..5a1db61 100644 --- a/lib/providers/main.dart +++ b/lib/providers/main.dart @@ -8,10 +8,17 @@ class GymLinkProvider with ChangeNotifier { bool get blackTheme => _blackTheme; ThemeData _theme = myTheme; ThemeData get theme => _theme; + void Function() _onError = () => {}; + + void Function() get onError => _onError; + void onTokenReceived(String token) { if (token == 'token123') { _isLoading = false; notifyListeners(); + } else { + _isLoading = true; + notifyListeners(); } } @@ -25,4 +32,8 @@ class GymLinkProvider with ChangeNotifier { _theme = theme; notifyListeners(); } + + void setOnError(void Function() onError) { + _onError = onError; + } } diff --git a/lib/states/mobile.dart b/lib/states/mobile.dart index bdbe5d2..7900cf8 100644 --- a/lib/states/mobile.dart +++ b/lib/states/mobile.dart @@ -14,12 +14,14 @@ class MyAppStateMobile extends State { final isLoading = provider.isLoading; return ChangeNotifierProvider( create: (_) => CartProvider(), - builder: (context, __) => MaterialApp( - title: 'GymLink Module', - theme: theme, - debugShowCheckedModeBanner: false, - home: MainPage(isLoading: isLoading), - ), + builder: (context, __) => isLoading + ? const Center(child: CircularProgressIndicator()) + : MaterialApp( + title: 'GymLink Module', + theme: theme, + debugShowCheckedModeBanner: false, + home: const MainPage(), + ), ); }, ); diff --git a/lib/states/web.dart b/lib/states/web.dart index fabf884..1a00024 100644 --- a/lib/states/web.dart +++ b/lib/states/web.dart @@ -6,13 +6,13 @@ 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/providers/cart.dart'; +import 'package:gymlink_module_web/providers/main.dart'; import 'package:gymlink_module_web/theme.dart'; import 'package:provider/provider.dart'; @js.JSExport() class MyAppStateWeb extends State { final _streamController = StreamController.broadcast(); - bool _isLoading = true; ThemeData theme = myTheme; bool black_theme = false; @@ -32,31 +32,33 @@ class MyAppStateWeb extends State { @override Widget build(BuildContext context) { + final theme = context.watch().theme; + final isLoading = context.watch().isLoading; return ChangeNotifierProvider( create: (_) => CartProvider(), - child: MaterialApp( - title: 'GymLink Module', - theme: theme, - debugShowCheckedModeBanner: false, - home: MainPage(isLoading: _isLoading), - ), + child: isLoading + ? const Center(child: CircularProgressIndicator()) + : MaterialApp( + title: 'GymLink Module', + theme: theme, + debugShowCheckedModeBanner: false, + home: const MainPage(), + ), ); } @js.JSExport() void onTokenReceived(String token) { - if (token == 'token123') { - setState(() { - _isLoading = false; - }); - } + context.read().onTokenReceived(token); } @js.JSExport() void changeColor(int color) { - setState(() { - black_theme = !black_theme; //FIXME: TEMPORARY - theme = getThemeData(Color(color), black_theme); - }); + context.read().changeTheme(color); + } + + @js.JSExport() + void setOnError(void Function() onError) { + context.read().setOnError(onError); } } diff --git a/web/js/demo-js-interop.js b/web/js/demo-js-interop.js index 1042d13..df8b7c7 100644 --- a/web/js/demo-js-interop.js +++ b/web/js/demo-js-interop.js @@ -1,31 +1,45 @@ -(function(){ - "use strict"; +(function () { + 'use strict'; window._stateSet = function () { - window._stateSet = function() { - console.log("Call _stateSet only once"); + window._stateSet = function () { + console.log('Call _stateSet only once'); }; let appState = window._appState; let btn = document.getElementById('token'); - btn.addEventListener('click', function() { + btn.addEventListener('click', function () { 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); + 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) - }) - + 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); + 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) - }) - } -}()); \ No newline at end of file + appState.changeColor(numColor); + }); + + function onError() { + console.error('aboba'); + } + + appState.setOnError(onError); + }; +})();