Skip to content

Instantly share code, notes, and snippets.

@slightfoot
Created October 15, 2025 19:19
Show Gist options
  • Save slightfoot/f9ba20a9685c621eb4ee91594c7dfe29 to your computer and use it in GitHub Desktop.
Save slightfoot/f9ba20a9685c621eb4ee91594c7dfe29 to your computer and use it in GitHub Desktop.
Share Image from app - by Simon Lightfoot :: #HumpdayQandA on 15th October 2025 :: https://www.youtube.com/watch?v=G5iipsrjb78
// MIT License
//
// Copyright (c) 2025 Simon Lightfoot
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import 'dart:async';
import 'dart:ui' as ui;
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test_app2/photos.dart';
import 'package:share_plus/share_plus.dart';
void main() {
runApp(const App());
}
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: const Home(),
);
}
}
class Home extends StatefulWidget {
const Home({super.key});
@override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
final _gridKey = GlobalKey(debugLabel: 'Grid');
final _itemKeys = <GlobalKey>[];
@override
void initState() {
super.initState();
for (int i = 0; i < imageThumbUrls.length; i++) {
_itemKeys.add(GlobalKey(debugLabel: 'Item $i'));
}
}
void _shareItemImage(int index) async {
final imagePngData = await fetchImageBytesFromImageProvider(
NetworkImage(imageThumbUrls[index]),
);
SharePlus.instance.share(
ShareParams(
text: 'I want to share my picture with you!',
files: [
XFile.fromData(
imagePngData,
mimeType: 'image/png',
name: 'image.png',
length: imagePngData.lengthInBytes,
),
],
),
);
}
Future<Uint8List> fetchImageBytesFromImageProvider(
ImageProvider imageProvider, {
ui.ImageByteFormat format = ui.ImageByteFormat.png,
}) async {
final completer = Completer<Uint8List>();
final imageStream = imageProvider.resolve(
createLocalImageConfiguration(context),
);
final listener = ImageStreamListener(
(ImageInfo image, bool sync) async {
final data = await image.image.toByteData(format: format);
completer.complete(data!.buffer.asUint8List());
},
onError: (Object error, StackTrace? stackTrace) {
completer.completeError(error, stackTrace);
},
);
imageStream.addListener(listener);
try {
final data = await completer.future;
return data;
} finally {
imageStream.removeListener(listener);
}
}
void _shareFromKey(GlobalKey key) async {
final imagePngData = await fetchImageForWidget(key);
SharePlus.instance.share(
ShareParams(
text: 'I want to share my picture with you!',
files: [
XFile.fromData(
imagePngData,
mimeType: 'image/png',
name: 'image.png',
length: imagePngData.lengthInBytes,
),
],
),
);
}
Future<Uint8List> fetchImageForWidget(
GlobalKey key, {
ui.ImageByteFormat format = ui.ImageByteFormat.png,
}) async {
final completer = Completer<Uint8List>();
final renderBoundary = key.currentContext!.findRenderObject()! as RenderRepaintBoundary;
WidgetsBinding.instance.addPostFrameCallback((_) async {
try {
final image = await renderBoundary.toImage();
final data = await image.toByteData(format: format);
completer.complete(data!.buffer.asUint8List());
} catch (error, stack) {
completer.completeError(error, stack);
}
});
return await completer.future;
}
@override
Widget build(BuildContext context) {
final viewPadding = MediaQuery.viewPaddingOf(context);
return Scaffold(
body: InkWell(
// onTap: () => _shareFromKey(_gridKey),
child: RepaintBoundary(
key: _gridKey,
child: GridView.builder(
padding: const EdgeInsets.all(4.0) + viewPadding,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
),
itemCount: imageThumbUrls.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.all(4.0),
child: RepaintBoundary(
key: _itemKeys[index],
child: Material(
borderRadius: BorderRadius.circular(12.0),
clipBehavior: Clip.antiAlias,
child: Ink.image(
image: NetworkImage(imageThumbUrls[index]),
fit: BoxFit.cover,
child: InkWell(
//onTap: () => _shareItemImage(index),
onTap: () => _shareFromKey(_itemKeys[index]),
child: Center(
child: Text(
'Item $index',
style: TextStyle(
color: Colors.white,
fontSize: 24.0,
fontWeight: FontWeight.w500,
),
),
),
),
),
),
),
);
},
),
),
),
);
}
}
///
/// This are PicasaWeb thumbnail URLs and could potentially change. Ideally the PicasaWeb API
/// should be used to fetch the URLs.
///
/// Credit to Romain Guy for the photos:
/// http://www.curious-creature.org/
/// https://plus.google.com/109538161516040592207/about
/// http://www.flickr.com/photos/romainguy
///
List<String> imageThumbUrls = [
"https://lh6.googleusercontent.com/-55osAWw3x0Q/URquUtcFr5I/AAAAAAAAAbs/rWlj1RUKrYI/s240-c/A%252520Photographer.jpg",
"https://lh4.googleusercontent.com/--dq8niRp7W4/URquVgmXvgI/AAAAAAAAAbs/-gnuLQfNnBA/s240-c/A%252520Song%252520of%252520Ice%252520and%252520Fire.jpg",
"https://lh5.googleusercontent.com/-7qZeDtRKFKc/URquWZT1gOI/AAAAAAAAAbs/hqWgteyNXsg/s240-c/Another%252520Rockaway%252520Sunset.jpg",
"https://lh3.googleusercontent.com/--L0Km39l5J8/URquXHGcdNI/AAAAAAAAAbs/3ZrSJNrSomQ/s240-c/Antelope%252520Butte.jpg",
"https://lh6.googleusercontent.com/-8HO-4vIFnlw/URquZnsFgtI/AAAAAAAAAbs/WT8jViTF7vw/s240-c/Antelope%252520Hallway.jpg",
"https://lh4.googleusercontent.com/-WIuWgVcU3Qw/URqubRVcj4I/AAAAAAAAAbs/YvbwgGjwdIQ/s240-c/Antelope%252520Walls.jpg",
"https://lh6.googleusercontent.com/-UBmLbPELvoQ/URqucCdv0kI/AAAAAAAAAbs/IdNhr2VQoQs/s240-c/Apre%2525CC%252580s%252520la%252520Pluie.jpg",
"https://lh3.googleusercontent.com/-s-AFpvgSeew/URquc6dF-JI/AAAAAAAAAbs/Mt3xNGRUd68/s240-c/Backlit%252520Cloud.jpg",
"https://lh5.googleusercontent.com/-bvmif9a9YOQ/URquea3heHI/AAAAAAAAAbs/rcr6wyeQtAo/s240-c/Bee%252520and%252520Flower.jpg",
"https://lh5.googleusercontent.com/-n7mdm7I7FGs/URqueT_BT-I/AAAAAAAAAbs/9MYmXlmpSAo/s240-c/Bonzai%252520Rock%252520Sunset.jpg",
"https://lh6.googleusercontent.com/-4CN4X4t0M1k/URqufPozWzI/AAAAAAAAAbs/8wK41lg1KPs/s240-c/Caterpillar.jpg",
"https://lh3.googleusercontent.com/-rrFnVC8xQEg/URqufdrLBaI/AAAAAAAAAbs/s69WYy_fl1E/s240-c/Chess.jpg",
"https://lh5.googleusercontent.com/-WVpRptWH8Yw/URqugh-QmDI/AAAAAAAAAbs/E-MgBgtlUWU/s240-c/Chihuly.jpg",
"https://lh5.googleusercontent.com/-0BDXkYmckbo/URquhKFW84I/AAAAAAAAAbs/ogQtHCTk2JQ/s240-c/Closed%252520Door.jpg",
"https://lh3.googleusercontent.com/-PyggXXZRykM/URquh-kVvoI/AAAAAAAAAbs/hFtDwhtrHHQ/s240-c/Colorado%252520River%252520Sunset.jpg",
"https://lh3.googleusercontent.com/-ZAs4dNZtALc/URquikvOCWI/AAAAAAAAAbs/DXz4h3dll1Y/s240-c/Colors%252520of%252520Autumn.jpg",
"https://lh4.googleusercontent.com/-GztnWEIiMz8/URqukVCU7bI/AAAAAAAAAbs/jo2Hjv6MZ6M/s240-c/Countryside.jpg",
"https://lh4.googleusercontent.com/-bEg9EZ9QoiM/URquklz3FGI/AAAAAAAAAbs/UUuv8Ac2BaE/s240-c/Death%252520Valley%252520-%252520Dunes.jpg",
"https://lh6.googleusercontent.com/-ijQJ8W68tEE/URqulGkvFEI/AAAAAAAAAbs/zPXvIwi_rFw/s240-c/Delicate%252520Arch.jpg",
"https://lh5.googleusercontent.com/-Oh8mMy2ieng/URqullDwehI/AAAAAAAAAbs/TbdeEfsaIZY/s240-c/Despair.jpg",
"https://lh5.googleusercontent.com/-gl0y4UiAOlk/URqumC_KjBI/AAAAAAAAAbs/PM1eT7dn4oo/s240-c/Eagle%252520Fall%252520Sunrise.jpg",
"https://lh3.googleusercontent.com/-hYYHd2_vXPQ/URqumtJa9eI/AAAAAAAAAbs/wAalXVkbSh0/s240-c/Electric%252520Storm.jpg",
"https://lh5.googleusercontent.com/-PyY_yiyjPTo/URqunUOhHFI/AAAAAAAAAbs/azZoULNuJXc/s240-c/False%252520Kiva.jpg",
"https://lh6.googleusercontent.com/-PYvLVdvXywk/URqunwd8hfI/AAAAAAAAAbs/qiMwgkFvf6I/s240-c/Fitzgerald%252520Streaks.jpg",
"https://lh4.googleusercontent.com/-KIR_UobIIqY/URquoCZ9SlI/AAAAAAAAAbs/Y4d4q8sXu4c/s240-c/Foggy%252520Sunset.jpg",
"https://lh6.googleusercontent.com/-9lzOk_OWZH0/URquoo4xYoI/AAAAAAAAAbs/AwgzHtNVCwU/s240-c/Frantic.jpg",
"https://lh3.googleusercontent.com/-0X3JNaKaz48/URqupH78wpI/AAAAAAAAAbs/lHXxu_zbH8s/s240-c/Golden%252520Gate%252520Afternoon.jpg",
"https://lh6.googleusercontent.com/-95sb5ag7ABc/URqupl95RDI/AAAAAAAAAbs/g73R20iVTRA/s240-c/Golden%252520Gate%252520Fog.jpg",
"https://lh3.googleusercontent.com/-JB9v6rtgHhk/URqup21F-zI/AAAAAAAAAbs/64Fb8qMZWXk/s240-c/Golden%252520Grass.jpg",
"https://lh4.googleusercontent.com/-EIBGfnuLtII/URquqVHwaRI/AAAAAAAAAbs/FA4McV2u8VE/s240-c/Grand%252520Teton.jpg",
"https://lh4.googleusercontent.com/-WoMxZvmN9nY/URquq1v2AoI/AAAAAAAAAbs/grj5uMhL6NA/s240-c/Grass%252520Closeup.jpg",
"https://lh3.googleusercontent.com/-6hZiEHXx64Q/URqurxvNdqI/AAAAAAAAAbs/kWMXM3o5OVI/s240-c/Green%252520Grass.jpg",
"https://lh5.googleusercontent.com/-6LVb9OXtQ60/URquteBFuKI/AAAAAAAAAbs/4F4kRgecwFs/s240-c/Hanging%252520Leaf.jpg",
"https://lh4.googleusercontent.com/-zAvf__52ONk/URqutT_IuxI/AAAAAAAAAbs/D_bcuc0thoU/s240-c/Highway%2525201.jpg",
"https://lh6.googleusercontent.com/-H4SrUg615rA/URquuL27fXI/AAAAAAAAAbs/4aEqJfiMsOU/s240-c/Horseshoe%252520Bend%252520Sunset.jpg",
];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment