this constitutes my first post here on StackOverflow. I have tried to figure the problem on my own, but I'm a new developer and even if I was to trial-and-error my way through, I don't think I'd have a good understanding of what's going wrong here.
Desired functionality here revolves around capturing a screenshot of an invisible widget in order to share it to social media platforms. Think sharing a song to your instagram story in Spotify or something.
The logic of the user journey is as follows: user ranks tiles in order to create a tier list. Upon finishing their rankings, a button appears for them to share the tiles they have put in the top tier (S-Tier):
//park_rank_view.dart
onPressed: () async {
final image = await controller.captureFromWidget(const InvisibleWidget(), delay: const Duration(seconds: 5), context: context);
// ignore: use_build_context_synchronously
shareImage(context, image);
}
This button awaits the screenshot controller provided in the same file final controller = ScreenshotController();
then captures the InvisibleWidget (different file) & finally shares it using share_plus package:
//invisible_widget.dart
Future shareImage(BuildContext context, Uint8List bytes) async {
final tempDir = await getApplicationDocumentsDirectory();
final image = File('${tempDir.path}/image.png');
// ignore: use_build_context_synchronously
final box = context.findRenderObject() as RenderBox?;
image.writeAsBytesSync(bytes);
await Share.shareXFiles([XFile(image.path)],
text: 'I just ranked the national parks!',
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size);
}
class InvisibleWidget extends StatefulWidget {
const InvisibleWidget({super.key});
@override
InvisibleWidgetState createState() => InvisibleWidgetState();
}
class InvisibleWidgetState extends State<InvisibleWidget> {
@override
Widget build(BuildContext context) {
return Builder(
builder: (context) {
return Screenshot(
controller: ScreenshotController(),
child: Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(
'assets/images/background.jpg'), // Your background image
fit: BoxFit.scaleDown,
),
),
child: Center(
child: SizedBox(
height: 1050,
width: 590,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Text('Check Out my S-Tier Parks 👀',
style: TextStyle(color: Colors.white, fontSize: 35)),
const SizedBox(height: 50),
SizedBox(
height: 920,
child: Consumer<ParkTileModel>(
builder: (context, tileContext, _) {
return GridView.count(
crossAxisCount: 4,
physics: const NeverScrollableScrollPhysics(),
mainAxisSpacing: 15,
crossAxisSpacing: 10,
children: tierSList(context),
);
}
),
)
],
),
),
),
),
);
}
);
}
}
This in turn builds the image based on the values provided by the user:
//build_origin.dart
List<Widget> tierSList(BuildContext context) {
final tierS = context.watch<ParkTileModel>().tieredTiles['S'] ?? [];
return tierS
.map((tile) => buildOrigin(context, tile, draggable: false))
.toList();
}
Providers have not yet been scoped, I plan to refactor later to be more efficient but for now a MultiProvider is wrapping the entire MaterialApp:
//main.dart
MultiProvider(
providers: [
ChangeNotifierProvider<ParkTileModel>(
create: (context) => ParkTileModel()
),
],
// child: MaterialApp(etc, etc)
);
It should be noted that I created a route for InvisibleWidget() in order to visualize it while I was building. When I view it through the route, the widget displays perfectly with all the tiles expected. Based on this, I suppose it would follow that the inability to find the correct Provider has something to do with the screenshot method itself.
This is my first "real" project which I hope to present to potential employers and I am a bit out of my depth here. Though, I suppose the whole point is to work through these concepts and learn bit by bit. Any assistance would be greatly appreciated and let me know if I haven't conformed to best practices for StackOverflow posts. I will attach the current condition of the image which includes the bulk of the error message.
The relevant error-causing widget was: Consumer in the invisible_widget.dart file.
"Share S-Tier Parks" Button Resulting Image
Let me know if there's any more context needed to squash this one. Thanks for your time.
You are creating the instance of InvisibleWidget
when invoking the controller.captureFromWidget
, which means that the widget is not part of your widgets-tree. Consequently, its BuildContext does not have access to the providers of your app.
One solution could be wrapping your widget around a Provider:
onPressed: () async {
final image = await controller.captureFromWidget(
const Provider<ParkTileModel>.value(
value: context.read<ParkTileModel>(),
child: InvisibleWidget(),
),
delay: const Duration(seconds: 5),
context:
context,
);
// ignore: use_build_context_synchronously
shareImage(context, image);
}