Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Flutter: Positioned Widgets Misalign on Scalable SVG Background in InteractiveViewer

I’m developing a Flutter app where I need to overlay icons on specific points on an SVG image. These icons represent locations which are normalized to (x,y) coordinates between 0 and 100 and must align accurately with what is on the image irrespective of screen size or the available space of the InteractiveViewer. However, I’m facing the issue where the icons misalign when the available space changes. I am trying it out on my phone and an emulator and the points are offset slightly, but clearly.

Below is a minimal example illustrating the problem. The goal is for the icon to stay aligned with a specific point on the SVG (at a normalized position of x = 50 = y) regardless of the available space for the InteractiveViewer.

import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: CustomInteractiveMap(),
        ),
      ),
    );
  }
}

class CustomInteractiveMap extends StatefulWidget {
  @override
  _CustomInteractiveMapState createState() => _CustomInteractiveMapState();
}

class _CustomInteractiveMapState extends State<CustomInteractiveMap> {

  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: LayoutBuilder(
        builder: (context, constraints) => InteractiveViewer(
          minScale: 1,
          maxScale: 5,
          child: Stack(
            children: [
              SvgPicture.asset('assets/images/background.svg', fit: BoxFit.contain),
              Positioned(
                left: (50 / 100) * constraints.maxWidth, // exemplary x coordinate of 50 (out of [0, 100])
                top: (50 / 100) * constraints.maxHeight, // exemplary y coordinate of 50 (out of [0, 100])
                child: const Icon(Icons.location_pin, color: Colors.redAccent),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

I have tried to wrap the LayoutBuilder in an AspectRatio like so: return Expanded(child: AspectRatio(aspectRatio: 1,child: LayoutBuilder(...) to make sure that the ratio of the available space has no impact. On first glance this improved the situation, but has not resolved the offset entirely.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Edit: I have also tried to use MediaQuery.of(context).size instead of using the LayoutBuilder, but this does not solve the situation either.

Any help will be highly appreciated! Thank you!

>Solution :

I’m not entirely sure if this is the problem but you can try it out. I believe the main problem is that the values you give to left and top in the positioned correspond to the top left corner of the icons. But ideally you would want the coordinates for the center of the icons because the centers might be different for different screens even with the same top-left coordinate. What might solve this is to translate the icons in such a way that their centers are now where their top-left corner used to be. Something like

          child: Stack(
            children: [
              SvgPicture.asset('assets/images/background.svg', fit: BoxFit.contain),
              Positioned(
                left: (50 / 100) * constraints.maxWidth, // exemplary x coordinate of 50 (out of [0, 100])
                top: (50 / 100) * constraints.maxHeight, // exemplary y coordinate of 50 (out of [0, 100])
                child: FractionalTranslation(
                    translation: const Offset(-0.5, -0.5),
                    child: const Icon(Icons.location_pin, color: Colors.redAccent),
                ),
              ),
            ],
          ),

You probably will need to figure out the correct coordinates again for all the icons, but I think that when you then get it right it will be right for all screen sizes. I haven’t tried it myself, so it’s a bit of a speculative answer, but it’s worth a try.

EDIT: given that it’s a Icons.location_pin which has the point down in the center you might want a const Offset(-0.5, -1) instead

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading