Irbisa · Cheatsheet

Images, Video & Camera

Image pipeline, ImageCache, decode sizing, video_player, camera, audio, EXIF, thumbnails

Middle Developer · 21 questions

  1. 01

    How does Flutter choose between 1x, 2x and 3x versions of an image asset, and what goes wrong if you ship only one?

    AssetImage looks up every variant of the path in the asset manifest, picks the one whose folder ratio (2.0x, 3.0x) is closest to the screen's device pixel ratio, and sets the image's scale, so the picture lays out at the same logical size on every screen.

    # pubspec.yaml: list the main file (or its folder); variants are found automaticallyflutter:  assets:    - assets/images/logo.png # On disk:…
  2. 02

    How do you display an SVG in Flutter, and why can't the Image widget show it?

    Use the flutter_svg package: SvgPicture.asset, SvgPicture.network or SvgPicture.string.

    import 'package:flutter_svg/flutter_svg.dart'; Widget emptyState() => SvgPicture.asset(      'assets/icons/empty_state.svg', // declared under flutter: assets:      width: 160,      colorFilter: const ColorFilter.mode(Colors.grey, BlendMode.srcIn),…
  3. 03

    How do you play an audio file in Flutter, and what must you do with the player when the screen closes?

    Use a package such as just_audio or audioplayers: create an AudioPlayer, give it a source (asset, file or URL), call play(), and call dispose() when its owner goes away, because the player holds a native decoder and the audio focus.

    class DingButton extends StatefulWidget {  const DingButton({super.key});   @override  State<DingButton> createState() => _DingButtonState();}…
  4. 04

    How does Image.network load and cache an image, and what roles do ImageProvider, ImageStream and ImageCache play?

    Image.network creates a NetworkImage, an ImageProvider that is a cache key plus a loader.

  5. 05

    A grid of full-resolution photos runs out of memory even though imageCache is capped at 100 MB. Why, and how do you fix it?

    The cap only controls what the cache may keep for later; an image a widget is showing is held by that widget, so evicting it frees nothing.

    class PhotoGrid extends StatelessWidget {  const PhotoGrid({super.key, required this.urls});  final List<String> urls;   @override  Widget build(BuildContext context) {…
  6. 06

    This avatar is sharp on an old phone but blurry on a new one, and some photos look squashed. What is wrong with the code?

    Widget avatar(String url) {  return ClipOval(    child: Image.network(      url,      width: 96,      height: 96,      fit: BoxFit.cover,      cacheWidth: 96,      cacheHeight: 96,    ),  );}

    cacheWidth and cacheHeight are physical pixels, not logical ones: on a 3x screen the 96-logical-pixel circle needs 288 physical pixels, so a 96-pixel bitmap is stretched and blurs.

  7. 07

    You precache the hero image like this, yet the detail page still shows the placeholder first. Why?

    Future<void> openDetails(BuildContext context, Product p) async {  await precacheImage(NetworkImage(p.heroUrl), context);  if (!context.mounted) return;  Navigator.of(context).push(MaterialPageRoute(    builder: (_) => Image.network(      p.heroUrl,      cacheWidth: 1080,      loadingBuilder: (context, child, progress) =>          progress == null ? child : const HeroPlaceholder(),    ),  ));}

    The precache stored the key of NetworkImage(url), but the page asks for ResizeImage(NetworkImage(url), width: 1080): cacheWidth changes the cache key, so the page misses the cache and starts a fresh download.

  8. 08

    What does cached_network_image add over Image.network, and what do you still have to configure yourself?

    A disk cache: through flutter_cache_manager it stores downloaded files in the app's cache directory, so after a restart images load from disk instead of the network.

    final feedCache = CacheManager(  Config(    'feedImages',    stalePeriod: const Duration(days: 7), // unused files are deleted after 7 days; freshness comes from Cache-Control    maxNrOfCacheObjects: 2000,  ),…
  9. 09

    Find the bug: after the user uploads a new avatar, the profile keeps showing the old photo until the app restarts.

    class _ProfileHeaderState extends State<ProfileHeader> {  late User _user = widget.user;   Future<void> _changeAvatar(Uint8List cropped) async {    // The server overwrites users/<id>/avatar.jpg and returns the updated user.    final updated = await widget.api.uploadAvatar(_user.id, cropped);    if (!mounted) return;    setState(() => _user = updated);  }   @override  Widget build(BuildContext context) => CircleAvatar(        radius: 48,        backgroundImage: NetworkImage(_user.avatarUrl),      );}

    The URL never changes, and NetworkImage uses the URL (plus scale) as its cache key: after setState the avatar gets an equal provider, so Flutter reuses the old decoded image and never downloads the new file.

  10. 10

    How do you let the user pick a photo or take one with image_picker, and which permissions does it need on iOS and Android?

    ImagePicker().pickImage(source: ...) opens the system photo picker or the camera app and returns an XFile pointing at a copy in a temporary directory, or null if the user cancels.

    final _picker = ImagePicker(); Future<XFile?> pickPhoto({required bool fromCamera}) {  return _picker.pickImage(    source: fromCamera ? ImageSource.camera : ImageSource.gallery,    maxWidth: 2048,             // resized natively, before Dart sees it…
  11. 11

    Photos uploaded from the phone show up rotated on the website. Why, and how do you fix it before upload?

    Cameras store pixels in the sensor's orientation and record the rotation in the EXIF Orientation tag.

    Future<Uint8List> prepareForUpload(String path) async {  final bytes = await FlutterImageCompress.compressWithFile(    path,    minWidth: 2048,  // shrinks until the shorter side is 2048; never upscales    minHeight: 2048,    quality: 82,…
  12. 12

    Compressing a photo before upload freezes the UI for a second or two. Why doesn't await help, and how do you fix it?

    Decoding, resizing and re-encoding a 12-megapixel photo in the pure-Dart image package is seconds of CPU work, and an async function still runs it on the UI isolate: await only yields at real asynchronous gaps.

    // wrong: runs on the UI isolate; no frames until it returns.Future<Uint8List> shrinkOnUiIsolate(Uint8List bytes) async {  final decoded = img.decodeImage(bytes)!;  return img.encodeJpg(img.copyResize(decoded, width: 2048), quality: 82);}…
  13. 13

    How do you play a video with video_player, and what do you need to handle in the controller's lifecycle?

    Create a VideoPlayerController (networkUrl, asset or file) in initState, call initialize(), show VideoPlayer(controller) inside an AspectRatio once value.isInitialized is true, and call dispose() in dispose.

    class ClipPlayer extends StatefulWidget {  const ClipPlayer({super.key, required this.url});  final Uri url;   @override  State<ClipPlayer> createState() => _ClipPlayerState();…
  14. 14

    This clip restarts from the beginning and memory grows every time the parent rebuilds. What is wrong with the widget?

    class ClipView extends StatelessWidget {  const ClipView({super.key, required this.url});  final Uri url;   @override  Widget build(BuildContext context) {    final controller = VideoPlayerController.networkUrl(url)      ..initialize()      ..play();    return AspectRatio(      aspectRatio: 16 / 9,      child: VideoPlayer(controller),    );  }}

    The controller, and with it a native player, is created inside build, so every rebuild makes a new one that starts from zero, and nothing ever calls dispose() on the old ones.

  15. 15

    What does it take for audio to keep playing with the screen locked and show lock-screen controls, on iOS, Android and in Dart?

    Three things: the OS must know you play audio in the background, there must be a media session the system can attach controls to, and the player must live outside the widget tree.

    // Info.plist: UIBackgroundModes = [audio]// AndroidManifest.xml: FOREGROUND_SERVICE and FOREGROUND_SERVICE_MEDIA_PLAYBACK,// plus audio_service's service with foregroundServiceType="mediaPlayback".late final AudioHandler audioHandler; Future<void> main() async {…
  16. 16

    Find the bug: after the app comes back from the background, the camera preview stays black and the log shows a CameraException.

    class _ScannerState extends State<Scanner> with WidgetsBindingObserver {  late final _camera =      CameraController(widget.camera, ResolutionPreset.medium)        ..initialize().then((_) => mounted ? setState(() {}) : null);  // initState: addObserver. dispose: removeObserver, _camera.dispose()  // build: CameraPreview(_camera)   @override  void didChangeAppLifecycleState(AppLifecycleState state) {    // free the camera for other apps    if (state == AppLifecycleState.inactive) _camera.dispose();    if (state == AppLifecycleState.resumed) {      _camera.initialize(); // take it back    }  }}

    On resumed the code calls initialize() on the controller it disposed on inactive.

  17. 17

    Where should image resizing happen (on the client, in a CDN image service, or in the upload pipeline), and why?

    Before the bytes reach the phone: a CDN image service or the upload pipeline produces sized variants, and the client requests the smallest one that covers its slot.

  18. 18

    Does video_player cover adaptive streaming (HLS/DASH) and DRM, and what do you use when it doesn't?

    Adaptive streaming, yes: point it at an HLS manifest and ExoPlayer or AVPlayer switch bitrates on their own.

  19. 19

    With the camera plugin, how do you analyse frames from startImageStream without making the preview stutter?

    The stutter comes from heavy per-frame work on the UI isolate: process one frame at a time off that isolate and drop the frames that arrive meanwhile.

    CameraController createController(CameraDescription camera) => CameraController(      camera,      ResolutionPreset.medium, // enough for detection, far less work per frame      enableAudio: false,      imageFormatGroup:          Platform.isAndroid ? ImageFormatGroup.nv21 : ImageFormatGroup.bgra8888,…
  20. 20

    To show video or camera frames, when do you use a Texture and when a platform view, and what does each cost per frame?

    Use a Texture by default: native code renders frames into a GPU surface the engine owns, and Flutter composites it like any layer, with no copy into Dart.

  21. 21

    How would you design offline downloads of video or podcast episodes: what to download, where to store it, and how to keep the storage bounded?

    Download one chosen rendition per episode as a whole file you own, write it to an app directory the OS will not purge or back up, and track every file in a small index with a byte budget and least-recently-played eviction.