HowTo Build a Flutter Viewer to Compare 2 Images

screen3

Using Flutter panorama_viewer, you can build a viewer to compare two images with different transforms. In the example below, the image on the lright has been modified with a artistic filter.

screen4

Code

code example

zoom lock

I have two PanoramaViewer panes. The state of each pane is assigned a global key. The current state of each pane can be acquired with nameOfKey.currentState You can set the zoom with the built-in setZoom() method.

void zoomIn() {
  final currentAState = _panoAKey.currentState;
  final currentBState = _panoBKey.currentState;

  if (currentAState != null) {
    final currentZoom = currentAState.scene!.camera.zoom;
    currentAState.setZoom(currentZoom + 0.3);
  }

  if (currentBState != null) {
    final currentZoom = currentBState.scene!.camera.zoom;
    currentBState.setZoom(currentZoom + 0.3);
  }
}

panning lock

When you move one of the panes, the other pane is automatically adjusted. This is again handled by the currentState built-in property. With the currentState, you can access latitudeRad and longitudeRad. The position or rotation of the sphere can be set with currentState.setView(). Pass the latitude and longitude to setView as degrees, not radians.

body: Listener(
  onPointerMove: (event) {
    final panoACurrentState = _panoAKey.currentState;
    final panoBCurrentState = _panoBKey.currentState;

    if (panoACurrentState != null) {
      _latitudeRad = panoACurrentState.latitudeRad;
      _longitudeRad = panoACurrentState.longitudeRad;
      _longitudeDeg = _longitudeRad * 180 / math.pi;
      _latitudeDeg = _latitudeRad * 180 / math.pi;
      // debugPrint('latitudeRad: $_latitudeRad');
      // debugPrint('longitudeRad: $_longitudeRad');
      if (panoBCurrentState != null) {
        // debugPrint('setting PanoramaB');
        panoBCurrentState.setView(_latitudeDeg, _longitudeDeg);
      }
    }
  },

summary of steps

  1. create two PanoramaViewer panes side-by-side
  2. set a GlobalKey for the PanoramaState for each PanoramaViewer pane
  3. get the current state of each pane
  4. for position, use setView and pass in the latitude and longitude
  5. for zoom, use setZoom
    1. if you’re using the mouse scroll room to handle the zoom, set a listener for onPointerSignal and then check for PointerScrollEvent. Grab the scroll with event.scrollDelta.dy
1 Like

Creating 2 PanoramaViewer panes side-by-side is a nice technique, thanks for posting. It does not appear like it’s a feature of PanoramaViewer to be able to compare. Enabling the panning lock really makes this useful for comparing the 2 images.

I know that construction companies (sometimes referred to as AEC or Architecture, Engineering and Construction companies) use 360 imagery heavily to track progress with construction sites. With this technique, I imagine they could compare a “before” and “after” for specific rooms or projects, and show that work is in progress or complete.

I believe Flutter is excellent for prototyping these types of applications. It’s cross platform but has a mobile-first philosophy, and works well for testing the THETA API and showing visual results. Is that why you picked Flutter to build this demo?

The panning lock is not a built-in feature and there is a slight lag between the two panes in my current implementation. The basic technique is that pane 1 updates pane 2. I may be able to change it in the future to have mouse movement update both panes rather than mouse update pane 1 and pane 1 update pane 2. With the zoom, the scroll wheel handles both zoom levels since there wasn’t mouse zoom built in.

Flutter may not be the best choice as there are more 360 viewers in JavaScript. I’m primarily using flutter because I build mobile app demos and games. Flutter has great reactiveness and response on mobile. Flutter does not have as much package support as JavaScript, but it works on my usage.

I may go back to React and JavaScript for different projects in the future.

1 Like

It’s cool that you were able to build those feature even though they not a part of the package.

I think also that the virtual staging, shown side by side, would be a powerful tool for selling in real estate.

The fact that the scroll wheel zoom was not part of the package is an interesting point about the challenges of flutter. Not only is panorama_viewer the only package for 360 image viewing in the Flutter space, it also only officially supports iOS and Android. Despite the lack of official support, panorama_viewer does work on desktop and web.

Since mobile is the only supported platform, panorama_viewer supports pinch-to-zoom by default.

There are many more 360 viewer packages for JavaScript and those packages are more likely to support features such as mouse scroll wheel zoom.

I believe you still have a fundamental question about why I use Flutter versus JavaScript when there are more JavaScript packages for 360 viewers.

As I’m not an experienced programmer, I like the safety bumpers of Dart that protect the programmer from themselves. There’s more features in Dart to catch programmer errors and help the programmer catch and correct their own errors. The obvious ones are static data types and null-safety.

There are several other more subtle reasons. For Dart, most people use the same toolchain to build and package Dart/Flutter apps. The choice is simple. Once you learn the a single Dart package system, you can continue to incrementally get deeper. In the case of JavaScript, there are multiple ways to package the app. If you type “alternatives to webpack” into Google, you’ll get a long list. For me, the challenge with JavaScript isn’t so much the language itself as the build systems. Then, there’s also the related build systems for the styling. Type in “alternatives to sass” to see what I mean.


Well, at the end of day, the main question is which toolset is going to increase the probability that you’ll finish the project. For me, Dart / Flutter is a fast way to finish mobile and desktop project.

In the case of the web, I prefer Python and Django.

Coincidentally, I just wrote an article on why I still use Python and Django for web development.

1 Like

For posting THETA images and information to the web, I’ve been part of a team using Django to build websites and web apps, and Django significantly helps with workflow and output. Here’s one example:

and another:

They are useful sites with searchable information, plus password protected dashboards for editing. And a lot more.

I like the Django community which includes good documentation, the “built-in” SQL database, and quite a few other features like templates and dashboards that make setting up internal admin panels easy.

I would like the web apps that I build to feel smoother. Do you have a preferred front-end architecture (like maybe React Native) that looks more modern?

I put a more extensive post in the comments here:

The main info is:

  • React - old
  • Astro - new
  • Ember - what discourse uses

However, I would challenge you on whether you need a “web app” or an “app”.

Using Flutter on the desktop is likely going to be better than a “web app” due to the limitations of browser technology. If the site is viewed in a mobile phone, the Flutter native app is definitely going to be better. This is the whole reason that native apps exist and the world hasn’t moved everything to web apps.

Why do you want to use a “web app” instead of an “app”?

Also, why use a “web app” instead of a progressive web app? The PWA is going to be more similar to a WASM app when the technology is ready for prime-time.

1 Like