diff --git a/lib/Components/CustomAppBar.dart b/lib/Components/CustomAppBar.dart index e5fe015..4604ad1 100644 --- a/lib/Components/CustomAppBar.dart +++ b/lib/Components/CustomAppBar.dart @@ -4,6 +4,7 @@ import 'package:mymuseum_visitapp/Components/AdminPopup.dart'; import 'package:mymuseum_visitapp/Components/LanguageSelection.dart'; import 'package:mymuseum_visitapp/Models/visitContext.dart'; import 'package:mymuseum_visitapp/Screens/Home/home.dart'; +import 'package:mymuseum_visitapp/Screens/Home/home_3.0.dart'; import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/constants.dart'; import 'package:provider/provider.dart'; @@ -77,7 +78,7 @@ class _CustomAppBarState extends State { visitAppContext.isScanningBeacons = false; //Navigator.of(context).pop(); Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute( - builder: (context) => const HomePage(), + builder: (context) => const HomePage3(), ),(route) => false); }); } @@ -96,14 +97,14 @@ class _CustomAppBarState extends State { child: visitAppContext.isMaximizeTextSize ? const Icon(Icons.text_fields, color: Colors.white) : const Icon(Icons.format_size, color: Colors.white) ), ), - Padding( + /*Padding( padding: const EdgeInsets.only(right: 5.0), child: SizedBox( width: 50, height: 50, child: LanguageSelection() ) - ), + ),*/ ], flexibleSpace: Container( decoration: const BoxDecoration( diff --git a/lib/Components/LanguageSelection.dart b/lib/Components/LanguageSelection.dart index 9fab41e..ea922e8 100644 --- a/lib/Components/LanguageSelection.dart +++ b/lib/Components/LanguageSelection.dart @@ -48,9 +48,10 @@ class _LanguageSelection extends State with TickerProviderSta } return PopupMenuButton( + color: kMainColor.withValues(alpha: 0.65), icon: Container( - height: size.height *0.07, - width: size.width *0.07, + height: size.height *0.08, + width: size.width *0.08, decoration: flagDecoration(selectedLanguage!), ), itemBuilder: (context){ diff --git a/lib/Screens/Home/configurations_list.dart b/lib/Screens/Home/configurations_list.dart index 6534e0a..76a31ef 100644 --- a/lib/Screens/Home/configurations_list.dart +++ b/lib/Screens/Home/configurations_list.dart @@ -79,7 +79,7 @@ class _ConfigurationsListState extends State { Navigator.of(context).pushReplacement(MaterialPageRoute( builder: (context) => - VisitPage(configurationId: configurations[index].id!, isAlreadyAllowed: visitAppContext.isScanBeaconAlreadyAllowed), + VisitPage(configuration: configurations[index], isAlreadyAllowed: visitAppContext.isScanBeaconAlreadyAllowed), )); } @@ -110,7 +110,7 @@ class _ConfigurationsListState extends State { Navigator.of(context).pushReplacement(MaterialPageRoute( builder: (context) => - VisitPage(configurationId: configurations[index].id!, isAlreadyAllowed: visitAppContext.isScanBeaconAlreadyAllowed), + VisitPage(configuration: configurations[index], isAlreadyAllowed: visitAppContext.isScanBeaconAlreadyAllowed), )); } } diff --git a/lib/Screens/Home/home_3.0.dart b/lib/Screens/Home/home_3.0.dart index 08f8777..9f2c581 100644 --- a/lib/Screens/Home/home_3.0.dart +++ b/lib/Screens/Home/home_3.0.dart @@ -8,6 +8,7 @@ import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:flutter_widget_from_html/flutter_widget_from_html.dart'; import 'package:manager_api_new/api.dart'; import 'package:mymuseum_visitapp/Components/CustomAppBar.dart'; +import 'package:mymuseum_visitapp/Components/LanguageSelection.dart'; import 'package:mymuseum_visitapp/Components/ScannerBouton.dart'; import 'package:mymuseum_visitapp/Components/loading_common.dart'; import 'package:mymuseum_visitapp/Helpers/DatabaseHelper.dart'; @@ -17,6 +18,7 @@ import 'package:mymuseum_visitapp/Helpers/requirement_state_controller.dart'; import 'package:mymuseum_visitapp/Helpers/translationHelper.dart'; import 'package:mymuseum_visitapp/Models/beaconSection.dart'; import 'package:mymuseum_visitapp/Models/visitContext.dart'; +import 'package:mymuseum_visitapp/Screens/Visit/visit.dart'; import 'package:mymuseum_visitapp/Services/apiService.dart'; import 'package:mymuseum_visitapp/Services/downloadConfiguration.dart'; import 'package:mymuseum_visitapp/app_context.dart'; @@ -50,6 +52,12 @@ class _HomePage3State extends State with WidgetsBindingObserver { Colors.red, ]; + @override + void initState() { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + super.initState(); + } + @override Widget build(BuildContext context) { Size size = MediaQuery.of(context).size; @@ -57,6 +65,7 @@ class _HomePage3State extends State with WidgetsBindingObserver { visitAppContext = appContext.getContext(); return Scaffold( + extendBody: true, body: FutureBuilder( future: getConfigurationsCall(visitAppContext.clientAPI, appContext), builder: (context, AsyncSnapshot snapshot) { @@ -67,16 +76,26 @@ class _HomePage3State extends State with WidgetsBindingObserver { return Stack( children: [ - SizedBox( + Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + colors: [Colors.grey, Colors.lightGreen], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + ), + ), + /*SizedBox( height: size.height * 0.35, width: size.width, child: Image.network( configurations[2].imageSource!, fit: BoxFit.cover, ) - ), - Padding( - padding: EdgeInsets.only(top: size.height * 0.15), + ),*/ + SafeArea( + top: false, + bottom: false, child: CustomScrollView( slivers: [ /*SliverAppBar( @@ -94,46 +113,72 @@ class _HomePage3State extends State with WidgetsBindingObserver { ),*/ SliverAppBar( backgroundColor: Colors.transparent, - pinned: true, - expandedHeight: 225.0, - flexibleSpace: Container() /*FlexibleSpaceBar( - collapseMode: CollapseMode.none, // 👈 Optionnel pour éviter le fade + pinned: false, + expandedHeight: 235.0, + flexibleSpace: FlexibleSpaceBar( + collapseMode: CollapseMode.pin, // 👈 Optionnel pour éviter le fade centerTitle: true, - background: Image.network( - configurations[2].imageSource!, - fit: BoxFit.cover, - ), - title: InkWell( - onLongPress: () { - showDialog( - builder: (BuildContext context) => const AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10.0)) - ), - //content: AdminPopup(), - contentPadding: EdgeInsets.zero, - ), context: context - ); - }, - child: SizedBox( - width: /*widget.isHomeButton ?*/ size.width * 0.8 /*: null*/, - height: 60, - child: Center( - child: HtmlWidget( - "Carnaval de Marche", - textStyle: const TextStyle(color: Colors.white, fontFamily: 'Roboto', fontSize: 20), - customStylesBuilder: (element) - { - return {'text-align': 'center', 'font-family': "Roboto", '-webkit-line-clamp': "2"}; - }, + background: Container( + padding: const EdgeInsets.only(bottom: 25.0), + decoration: const BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(25), + bottomRight: Radius.circular(25), + ), + boxShadow: [ + BoxShadow( + color: Colors.black26, + blurRadius: 3.5, + offset: Offset(0, -20), ), + ], + ), + child: ClipRRect( + borderRadius: const BorderRadius.only( + bottomLeft: Radius.circular(25), + bottomRight: Radius.circular(25), + ), + child: Stack( + fit: StackFit.expand, + children: [ + Opacity( + opacity: 0.85, + child: Image.network( + configurations[1].imageSource!, + fit: BoxFit.cover, + ), + ), + Positioned( + top: 35, + right: 10, + child: SizedBox( + width: 75, + height: 75, + child: LanguageSelection() + ), + ), + ], ), ), ), - ),*/ // plus de FlexibleSpaceBar + title: SizedBox( + width: /*widget.isHomeButton ?*/ size.width * 1.0 /*: null*/, + height: 120, + child: Center( + child: HtmlWidget( + configurations[0].title!.firstWhere((t) => t.language == "FR").value!, + textStyle: const TextStyle(color: Colors.white, fontFamily: 'Roboto', fontSize: 20), + customStylesBuilder: (element) + { + return {'text-align': 'center', 'font-family': "Roboto", '-webkit-line-clamp': "2"}; + }, + ), + ), + ), + ), // plus de FlexibleSpaceBar ), SliverPadding( - padding: const EdgeInsets.all(8), + padding: const EdgeInsets.only(left: 8.0, right: 8.0, top: 8.0, bottom: 20.0), sliver: SliverMasonryGrid.count( crossAxisCount: 2, mainAxisSpacing: 12, @@ -141,16 +186,54 @@ class _HomePage3State extends State with WidgetsBindingObserver { childCount: configurations.length, itemBuilder: (context, index) { //return buildTile(configurations[index]); - return Container( - height: 200 + (index % 3) * 55, - decoration: BoxDecoration( - color: colors[index % colors.length], - borderRadius: BorderRadius.circular(16), - ), - child: Center( - child: Text( - 'Bloc ${index + 1}', - style: const TextStyle(color: Colors.white, fontSize: 18), + String cleanedTitle = configurations[index].title!.firstWhere((t) => t.language == "FR").value!.replaceAll('\n', ' ').replaceAll('
', ' '); + + return InkWell( + onTap: () { + // Update context (it's in visit now, hero.. + + + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => + VisitPage(configuration: configurations[index], isAlreadyAllowed: visitAppContext.isScanBeaconAlreadyAllowed), + )); + }, + child: Hero( + tag: configurations[index].id!, + child: Container( + height: 200 + (index % 3) * 55, + decoration: BoxDecoration( + color: colors[index % colors.length], + borderRadius: BorderRadius.circular(16), + boxShadow: const [ + BoxShadow( + color: kMainGrey, + spreadRadius: 0.5, + blurRadius: 5, + offset: Offset(0, 1), // changes position of shadow + ), + ], + image: configurations[index].imageSource != null ? DecorationImage( + fit: BoxFit.cover, + opacity: 0.5, + image: NetworkImage( + configurations[index].imageSource!, + ), + ): null, + ), + child: Center( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: HtmlWidget( + cleanedTitle, + textStyle: const TextStyle(color: Colors.white, fontFamily: 'Roboto', fontSize: 20), + customStylesBuilder: (element) + { + return {'text-align': 'center', 'font-family': "Roboto", '-webkit-line-clamp': "2"}; + }, + ), + ), + ), ), ), ); diff --git a/lib/Screens/Visit/components/body.dart b/lib/Screens/Visit/components/body.dart index 5c4d4b0..4f2bdac 100644 --- a/lib/Screens/Visit/components/body.dart +++ b/lib/Screens/Visit/components/body.dart @@ -8,6 +8,7 @@ import 'package:mymuseum_visitapp/Helpers/DatabaseHelper.dart'; import 'package:mymuseum_visitapp/Helpers/translationHelper.dart'; import 'package:mymuseum_visitapp/Models/visitContext.dart'; import 'package:mymuseum_visitapp/Screens/Article/article_page.dart'; +import 'package:mymuseum_visitapp/Screens/Home/home_3.0.dart'; import 'package:mymuseum_visitapp/Screens/Quizz/quizz_page.dart'; import 'package:mymuseum_visitapp/Screens/section_page.dart'; import 'package:mymuseum_visitapp/Services/apiService.dart'; @@ -18,9 +19,9 @@ import 'package:provider/provider.dart'; import 'section_card.dart'; class Body extends StatefulWidget { - const Body({Key? key, required this.configurationId}) : super(key: key); + const Body({Key? key, required this.configuration}) : super(key: key); - final String? configurationId; + final ConfigurationDTO configuration; @override State createState() => _BodyState(); @@ -35,110 +36,175 @@ class _BodyState extends State { @override Widget build(BuildContext context) { final appContext = Provider.of(context); + VisitAppContext visitAppContext = appContext.getContext(); Size size = MediaQuery.of(context).size; return SafeArea( bottom: false, - child: Column( - children: [ - Row( - children: [ - SearchBox(onChanged: (value) { - setState(() { - if(value != null && value != "") { - searchValue = value; - } else { - searchValue = null; - } - }); - }), + top: false, + child: Stack( + children: [ + Hero( + tag: widget.configuration.id!, + child: Container( + height: size.height * 0.28, + decoration: BoxDecoration( + boxShadow: const [ + BoxShadow( + color: kMainGrey, + spreadRadius: 0.5, + blurRadius: 5, + offset: Offset(0, 1), // changes position of shadow + ), + ], + image: widget.configuration.imageSource != null ? DecorationImage( + fit: BoxFit.cover, + opacity: 0.65, + image: NetworkImage( + widget.configuration.imageSource!, + ), + ): null, + ), + ), + ), + Column( + children: [ + SizedBox( + height: size.height * 0.11, + width: size.width, + child: Stack( + fit: StackFit.expand, + children: [ + Positioned( + top: 35, + left: 10, + child: SizedBox( + width: 50, + height: 50, + child: InkWell( + onTap: () { + setState(() { + visitAppContext.configuration = null; + visitAppContext.isScanningBeacons = false; + Navigator.of(context).pop(); + /*Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute( + builder: (context) => const HomePage3(), + ),(route) => false);*/ + }); + }, + child: Container( + decoration: const BoxDecoration( + color: kMainColor, + shape: BoxShape.circle, + ), + child: const Icon(Icons.chevron_left, size: 28, color: Colors.white) + ), + ) + ), + ), + ], + ), + ), + Row( + children: [ + SearchBox(onChanged: (value) { + setState(() { + if(value != null && value != "") { + searchValue = value; + } else { + searchValue = null; + } + }); + }), + Expanded( + child: SearchNumberBox(onChanged: (value) { + setState(() { + if(value != null && value != "") { + searchNumberValue = int.parse(value); + } else { + searchNumberValue = null; + FocusScope.of(context).unfocus(); + } + }); + }), + ), + ], + ), + //const SizedBox(height: kDefaultPadding / 2), Expanded( - child: SearchNumberBox(onChanged: (value) { - setState(() { - if(value != null && value != "") { - searchNumberValue = int.parse(value); - } else { - searchNumberValue = null; - FocusScope.of(context).unfocus(); - } - }); - }), + child: Stack( + children: [ + // Our background + Container( + margin: const EdgeInsets.only(top: 0), + decoration: const BoxDecoration( + color: kBackgroundColor, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(40), + topRight: Radius.circular(40), + ), + ), + ), + FutureBuilder( + future: getSections(appContext), + builder: (context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + /*print("SECTIONTODISPA"); + print(sectionsToDisplay);*/ + return Padding( + padding: const EdgeInsets.only(bottom: 0), + child: RefreshIndicator( + onRefresh: () async { + if(!widget.configuration.isOffline!) { + // Force refresh if online + setState(() {}); + } }, + child: ListView.builder( + itemCount: sectionsToDisplay.length, + itemBuilder: (context, index) => SectionCard( + itemCount: sectionsToDisplay.length, + itemIndex: index, + sectionDTO: sectionsToDisplay[index], + press: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => SectionPage( + visitAppContextIn: appContext.getContext(), + sectionId: sectionsToDisplay[index].id!, + ), + ), + ); + }, + ), + ), + ), + ); + } else if (snapshot.connectionState == ConnectionState.none) { + return Text(TranslationHelper.getFromLocale("noData", appContext.getContext())); + } else { + return Center( + child: SizedBox( + height: size.height * 0.15, + child: const LoadingCommon() + ) + ); + } + } + ) + ], + ), ), ], ), - //const SizedBox(height: kDefaultPadding / 2), - Expanded( - child: Stack( - children: [ - // Our background - Container( - margin: const EdgeInsets.only(top: 0), - decoration: const BoxDecoration( - color: kBackgroundColor, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(40), - topRight: Radius.circular(40), - ), - ), - ), - FutureBuilder( - future: getSections(appContext), - builder: (context, AsyncSnapshot snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - /*print("SECTIONTODISPA"); - print(sectionsToDisplay);*/ - return Padding( - padding: const EdgeInsets.only(bottom: 0), - child: RefreshIndicator( - onRefresh: () async { - if(!(appContext.getContext() as VisitAppContext).configuration!.isOffline!) { - // Force refresh if online - setState(() {}); - } }, - child: ListView.builder( - itemCount: sectionsToDisplay.length, - itemBuilder: (context, index) => SectionCard( - itemCount: sectionsToDisplay.length, - itemIndex: index, - sectionDTO: sectionsToDisplay[index], - press: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => SectionPage( - visitAppContextIn: appContext.getContext(), - sectionId: sectionsToDisplay[index].id!, - ), - ), - ); - }, - ), - ), - ), - ); - } else if (snapshot.connectionState == ConnectionState.none) { - return Text(TranslationHelper.getFromLocale("noData", appContext.getContext())); - } else { - return Center( - child: SizedBox( - height: size.height * 0.15, - child: const LoadingCommon() - ) - ); - } - } - ) - ], - ), - ), ], ), ); } getSections(AppContext appContext) async { - VisitAppContext visitAppContext = (appContext.getContext() as VisitAppContext); - if(visitAppContext.configuration!.isOffline!) + VisitAppContext visitAppContext = appContext.getContext(); + if(widget.configuration.isOffline!) { // OFFLINE sections = List.from(await DatabaseHelper.instance.getData(DatabaseTableType.sections)); @@ -146,15 +212,15 @@ class _BodyState extends State { else { // ONLINE - List? sectionsDownloaded = await ApiService.getAllSections(visitAppContext.clientAPI, visitAppContext.configuration!.id!); + List? sectionsDownloaded = await ApiService.getAllSections(visitAppContext.clientAPI, widget.configuration.id!); //print(sectionsDownloaded); if(sectionsDownloaded!.isNotEmpty) { - sections = sectionsDownloaded.where((s) => s.type == SectionType.Article || s.type == SectionType.Quiz).toList(); // TODO Support more than Article and Quizz section type + sections = sectionsDownloaded.toList(); //print(sections); } } - sections = sections.where((s) => s.configurationId == widget.configurationId).toList(); + sections = sections.where((s) => s.configurationId == widget.configuration.id!).toList(); sections.sort((a,b) => a.order!.compareTo(b.order!)); sectionsToDisplay = sections; diff --git a/lib/Screens/Visit/components/section_card.dart b/lib/Screens/Visit/components/section_card.dart index 7c43b03..e9f399d 100644 --- a/lib/Screens/Visit/components/section_card.dart +++ b/lib/Screens/Visit/components/section_card.dart @@ -33,7 +33,7 @@ class SectionCard extends StatelessWidget { Size size = MediaQuery.of(context).size; final appContext = Provider.of(context); VisitAppContext visitAppContext = appContext.getContext(); - bool isOffline = (appContext.getContext() as VisitAppContext).configuration!.isOffline!; + bool isOffline = (appContext.getContext() as VisitAppContext).configuration?.isOffline! ?? false; return Container( margin: EdgeInsets.only( @@ -69,7 +69,7 @@ class SectionCard extends StatelessWidget { ), ), ), - if(sectionDTO.imageId != null) + if(sectionDTO.imageId != null && (appContext.getContext() as VisitAppContext).configuration != null) // section main image Positioned( top: kDefaultPadding +4.0, diff --git a/lib/Screens/Visit/visit.dart b/lib/Screens/Visit/visit.dart index 579b051..46ef418 100644 --- a/lib/Screens/Visit/visit.dart +++ b/lib/Screens/Visit/visit.dart @@ -25,9 +25,9 @@ import 'package:provider/provider.dart'; import 'components/body.dart'; class VisitPage extends StatefulWidget { - const VisitPage({Key? key, required this.configurationId, required this.isAlreadyAllowed}) : super(key: key); + const VisitPage({Key? key,required this.configuration, required this.isAlreadyAllowed}) : super(key: key); - final String configurationId; + final ConfigurationDTO configuration; final bool isAlreadyAllowed; @override @@ -35,7 +35,7 @@ class VisitPage extends StatefulWidget { } class _VisitPageState extends State with WidgetsBindingObserver { - ConfigurationDTO? configuration; + //ConfigurationDTO? configuration; int timeBetweenBeaconPopUp = 20000; // 20 sec int meterToBeacon = 100; // 15 meters @@ -73,6 +73,10 @@ class _VisitPageState extends State with WidgetsBindingObserver { if(widget.isAlreadyAllowed) { listeningState(); } + + /*WidgetsBinding.instance.addPostFrameCallback((_) { + final appContext = Provider.of(context, listen: false); + });*/ //listeningState(); } @@ -320,7 +324,7 @@ class _VisitPageState extends State with WidgetsBindingObserver { Widget build(BuildContext context) { final appContext = Provider.of(context); VisitAppContext visitAppContext = appContext.getContext(); - configuration = visitAppContext.configuration; + //configuration = visitAppContext.configuration; listener = controller.startStream.listen((flag) async { print(flag); @@ -331,17 +335,15 @@ class _VisitPageState extends State with WidgetsBindingObserver { } }); - - return PopScope( canPop: false, child: Scaffold( - appBar: CustomAppBar( + /*appBar: CustomAppBar( title: TranslationHelper.get(configuration!.title, visitAppContext), isHomeButton: true, - ), + ),*/ backgroundColor: kBackgroundGrey, - body: Body(configurationId: configuration!.id), // TODO handle error.. + body: Body(configuration: widget.configuration), floatingActionButton: Stack( children: [ visitAppContext.beaconSections != null && visitAppContext.beaconSections!.where((bs) => bs!.configurationId == visitAppContext.configuration!.id).isNotEmpty ? Align( diff --git a/lib/main.dart b/lib/main.dart index 33cd722..76d035d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -72,6 +72,12 @@ void main() async { //); //AudioPlayer.global.setGlobalAudioContext(audioContext); + WidgetsFlutterBinding.ensureInitialized(); + SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( + systemNavigationBarColor: Colors.transparent, // ← important + statusBarColor: Colors.transparent, + )); + runApp(myApp); }