357 lines
13 KiB
Dart
357 lines
13 KiB
Dart
import 'dart:convert';
|
|
import 'dart:async';
|
|
import 'dart:io';
|
|
|
|
import 'package:cached_network_image/cached_network_image.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
|
|
import 'package:manager_api_new/api.dart';
|
|
import 'package:mymuseum_visitapp/Components/loading_common.dart';
|
|
import 'package:mymuseum_visitapp/Helpers/translationHelper.dart';
|
|
import 'package:mymuseum_visitapp/Models/visitContext.dart';
|
|
import 'package:mymuseum_visitapp/Screens/Sections/Puzzle/message_dialog.dart';
|
|
import 'package:mymuseum_visitapp/app_context.dart';
|
|
import 'package:mymuseum_visitapp/constants.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'puzzle_piece.dart';
|
|
|
|
const IMAGE_PATH = 'image_path';
|
|
|
|
class PuzzlePage extends StatefulWidget {
|
|
final PuzzleDTO section;
|
|
PuzzlePage({required this.section});
|
|
|
|
@override
|
|
_PuzzlePage createState() => _PuzzlePage();
|
|
}
|
|
|
|
class _PuzzlePage extends State<PuzzlePage> {
|
|
PuzzleDTO puzzleDTO = PuzzleDTO();
|
|
|
|
int allInPlaceCount = 0;
|
|
bool isFinished = false;
|
|
GlobalKey _widgetKey = GlobalKey();
|
|
Size? realWidgetSize;
|
|
List<Widget> pieces = [];
|
|
|
|
bool isSplittingImage = true;
|
|
|
|
@override
|
|
void initState() {
|
|
//puzzleDTO = PuzzleDTO.fromJson(jsonDecode(widget.section!.data!))!;
|
|
puzzleDTO = widget.section;
|
|
puzzleDTO.rows = puzzleDTO.rows ?? 3;
|
|
puzzleDTO.cols = puzzleDTO.cols ?? 3;
|
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
|
Size size = MediaQuery.of(context).size;
|
|
final appContext = Provider.of<AppContext>(context, listen: false);
|
|
VisitAppContext visitAppContext = appContext.getContext();
|
|
|
|
print(puzzleDTO.messageDebut);
|
|
TranslationAndResourceDTO? messageDebut = puzzleDTO.messageDebut != null && puzzleDTO.messageDebut!.isNotEmpty ? puzzleDTO.messageDebut!.where((message) => message.language!.toUpperCase() == visitAppContext.language!.toUpperCase()).firstOrNull : null;
|
|
|
|
//await Future.delayed(const Duration(milliseconds: 50));
|
|
|
|
await WidgetsBinding.instance.endOfFrame;
|
|
getRealWidgetSize();
|
|
|
|
if(puzzleDTO.puzzleImage != null && puzzleDTO.puzzleImage!.url != null) {
|
|
//splitImage(Image.network(puzzleDTO.image!.resourceUrl!));
|
|
splitImage(CachedNetworkImage(
|
|
imageUrl: puzzleDTO.puzzleImage!.url!,
|
|
fit: BoxFit.fill,
|
|
errorWidget: (context, url, error) => Icon(Icons.error),
|
|
));
|
|
} else {
|
|
setState(() {
|
|
isSplittingImage = false;
|
|
});
|
|
}
|
|
|
|
if(messageDebut != null) {
|
|
showMessage(messageDebut, appContext, context, size);
|
|
}
|
|
});
|
|
|
|
super.initState();
|
|
}
|
|
|
|
Future<void> getRealWidgetSize() async {
|
|
RenderBox renderBox = _widgetKey.currentContext?.findRenderObject() as RenderBox;
|
|
Size size = renderBox.size;
|
|
|
|
setState(() {
|
|
realWidgetSize = size;
|
|
});
|
|
print("Taille réelle du widget : $size");
|
|
}
|
|
|
|
// we need to find out the image size, to be used in the PuzzlePiece widget
|
|
/*Future<Size> getImageSize(CachedNetworkImage image) async {
|
|
Completer<Size> completer = Completer<Size>();
|
|
|
|
/*image.image
|
|
.resolve(const ImageConfiguration())
|
|
.addListener(ImageStreamListener((ImageInfo info, bool _) {
|
|
completer.complete(
|
|
Size(info.image.width.toDouble(), info.image.height.toDouble()));
|
|
}));*/
|
|
|
|
CachedNetworkImage(
|
|
imageUrl: 'https://example.com/image.jpg',
|
|
placeholder: (context, url) => CircularProgressIndicator(),
|
|
errorWidget: (context, url, error) => Icon(Icons.error),
|
|
imageBuilder: (BuildContext context, ImageProvider imageProvider) {
|
|
Completer<Size> completer = Completer<Size>();
|
|
|
|
imageProvider
|
|
.resolve(const ImageConfiguration())
|
|
.addListener(ImageStreamListener((ImageInfo info, bool _) {
|
|
completer.complete(
|
|
Size(info.image.width.toDouble(), info.image.height.toDouble()));
|
|
}));
|
|
|
|
return CachedNetworkImage(
|
|
imageUrl: 'https://example.com/image.jpg',
|
|
placeholder: (context, url) => CircularProgressIndicator(),
|
|
errorWidget: (context, url, error) => Icon(Icons.error),
|
|
imageBuilder: (context, imageProvider) {
|
|
return Image(
|
|
image: imageProvider,
|
|
loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent? loadingProgress) {
|
|
if (loadingProgress == null) {
|
|
return child;
|
|
} else {
|
|
return Center(
|
|
child: CircularProgressIndicator(
|
|
value: loadingProgress.expectedTotalBytes != null
|
|
? loadingProgress.cumulativeBytesLoaded / (loadingProgress.expectedTotalBytes ?? 1)
|
|
: null,
|
|
),
|
|
);
|
|
}
|
|
},
|
|
);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
|
|
Size imageSize = await completer.future;
|
|
|
|
return imageSize;
|
|
}*/
|
|
|
|
// here we will split the image into small pieces
|
|
// using the rows and columns defined above; each piece will be added to a stack
|
|
void splitImage(CachedNetworkImage image) async {
|
|
//Size imageSize = await getImageSize(image);
|
|
//imageSize = realWidgetSize!;
|
|
Size imageSize = Size(realWidgetSize!.width * 1.25, realWidgetSize!.height * 1.25);
|
|
|
|
for (int x = 0; x < puzzleDTO.rows!; x++) {
|
|
for (int y = 0; y < puzzleDTO.cols!; y++) {
|
|
setState(() {
|
|
pieces.add(
|
|
PuzzlePiece(
|
|
key: GlobalKey(),
|
|
image: image,
|
|
imageSize: imageSize,
|
|
row: x,
|
|
col: y,
|
|
maxRow: puzzleDTO.rows!,
|
|
maxCol: puzzleDTO.cols!,
|
|
bringToTop: bringToTop,
|
|
sendToBack: sendToBack,
|
|
),
|
|
);
|
|
});
|
|
}
|
|
}
|
|
|
|
setState(() {
|
|
isSplittingImage = false;
|
|
});
|
|
}
|
|
|
|
// when the pan of a piece starts, we need to bring it to the front of the stack
|
|
void bringToTop(Widget widget) {
|
|
setState(() {
|
|
pieces.remove(widget);
|
|
pieces.add(widget);
|
|
});
|
|
}
|
|
|
|
// when a piece reaches its final position,
|
|
// it will be sent to the back of the stack to not get in the way of other, still movable, pieces
|
|
void sendToBack(Widget widget) {
|
|
setState(() {
|
|
allInPlaceCount++;
|
|
isFinished = allInPlaceCount == puzzleDTO.rows! * puzzleDTO.cols!;
|
|
pieces.remove(widget);
|
|
pieces.insert(0, widget);
|
|
|
|
if(isFinished) {
|
|
Size size = MediaQuery.of(context).size;
|
|
final appContext = Provider.of<AppContext>(context, listen: false);
|
|
VisitAppContext visitAppContext = appContext.getContext();
|
|
TranslationAndResourceDTO? messageFin = puzzleDTO.messageFin != null && puzzleDTO.messageFin!.isNotEmpty ? puzzleDTO.messageFin!.where((message) => message.language!.toUpperCase() == visitAppContext.language!.toUpperCase()).firstOrNull : null;
|
|
|
|
if(messageFin != null) {
|
|
showMessage(messageFin, appContext, context, size);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final appContext = Provider.of<AppContext>(context);
|
|
VisitAppContext visitAppContext = appContext.getContext();
|
|
Size size = MediaQuery.of(context).size;
|
|
var title = TranslationHelper.get(widget.section.title, appContext.getContext());
|
|
String cleanedTitle = title.replaceAll('\n', ' ').replaceAll('<br>', ' ');
|
|
|
|
return Stack(
|
|
children: [
|
|
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
|
|
),
|
|
],
|
|
gradient: const LinearGradient(
|
|
begin: Alignment.centerRight,
|
|
end: Alignment.centerLeft,
|
|
colors: [
|
|
/*Color(0xFFDD79C2),
|
|
Color(0xFFB65FBE),
|
|
Color(0xFF9146BA),
|
|
Color(0xFF7633B8),
|
|
Color(0xFF6528B6),
|
|
Color(0xFF6025B6)*/
|
|
kMainColor0, //Color(0xFFf6b3c4)
|
|
kMainColor1,
|
|
kMainColor2,
|
|
|
|
],
|
|
),
|
|
image: widget.section.imageSource != null ? DecorationImage(
|
|
fit: BoxFit.cover,
|
|
opacity: 0.65,
|
|
image: NetworkImage(
|
|
widget.section.imageSource!,
|
|
),
|
|
): null,
|
|
),
|
|
),
|
|
Column(
|
|
children: <Widget>[
|
|
SizedBox(
|
|
height: size.height * 0.11,
|
|
width: size.width,
|
|
child: Stack(
|
|
fit: StackFit.expand,
|
|
children: [
|
|
Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(top: 22.0),
|
|
child: SizedBox(
|
|
width: size.width *0.7,
|
|
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"};
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
Positioned(
|
|
top: 35,
|
|
left: 10,
|
|
child: SizedBox(
|
|
width: 50,
|
|
height: 50,
|
|
child: InkWell(
|
|
onTap: () {
|
|
Navigator.of(context).pop();
|
|
},
|
|
child: Container(
|
|
decoration: const BoxDecoration(
|
|
color: kMainColor,
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: const Icon(Icons.arrow_back, size: 23, color: Colors.white)
|
|
),
|
|
)
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Expanded(
|
|
child: Container(
|
|
margin: const EdgeInsets.only(top: 0),
|
|
decoration: const BoxDecoration(
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: kMainGrey,
|
|
spreadRadius: 0.5,
|
|
blurRadius: 2,
|
|
offset: Offset(0, 1), // changes position of shadow
|
|
),
|
|
],
|
|
color: kBackgroundColor,
|
|
borderRadius: BorderRadius.only(
|
|
topLeft: Radius.circular(30),
|
|
topRight: Radius.circular(30),
|
|
),
|
|
),
|
|
child: ClipRRect(
|
|
borderRadius: const BorderRadius.only(
|
|
topLeft: Radius.circular(30),
|
|
topRight: Radius.circular(30),
|
|
),
|
|
child: Center(
|
|
//color: Colors.green,
|
|
child: Container(
|
|
//color: Colors.green,
|
|
child: Padding(
|
|
key: _widgetKey,
|
|
padding: const EdgeInsets.all(0.0),
|
|
child: isSplittingImage ? Center(child: LoadingCommon()) :
|
|
puzzleDTO.puzzleImage == null || puzzleDTO.puzzleImage!.url == null || realWidgetSize == null
|
|
? Center(child: Text("Aucune image à afficher", style: TextStyle(fontSize: kNoneInfoOrIncorrect)))
|
|
: Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(0.0),
|
|
child: Container(
|
|
width: visitAppContext.puzzleSize != null && visitAppContext.puzzleSize!.width > 0 ? visitAppContext.puzzleSize!.width : realWidgetSize!.width * 0.8,
|
|
height: visitAppContext.puzzleSize != null && visitAppContext.puzzleSize!.height > 0 ? visitAppContext.puzzleSize!.height +1.5 : realWidgetSize!.height * 0.85,
|
|
child: Stack(
|
|
children: pieces,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
)
|
|
)
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
}
|
|
} |