CoderUni / responsive_sizer

Responsive Sizer helps implement are responsive layout by providing helper widgets and extensions

Home Page:https://pub.dev/packages/responsive_sizer

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Sizes do not appear to be updating correctly on Windows maximise or restore using WidgetsBindingObserver

zebulon98 opened this issue · comments

I have identified a potential issue where the responsive_sizer values are not updated correctly after clicking the Maximise or Restore Down buttons in a Flutter app executed either in a web browser or as a windows application. I have included test code to demonstrate the issue below. This uses responsive_sizer version 3.0.5+1

This app displays the screen dimensions along with the responsive_sizer (_widgetheight and _widgetwidth) dimensions using the WidgetsBindingObserver mixin to track changes to the window size.

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

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Window Resize Test',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: ResponsiveSizer(
        builder: (context, orientation, screenType) {
          return const MyHomePage(title: 'Window Resize Test');
        },
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
  late double _screenHeight;
  late double _screenWidth;
  late double _widgetHeight;
  late double _widgetWidth;

  @override
  void initState() {
    super.initState();

    // Add an observer to check for changes in screen size/orientation
    WidgetsBinding.instance?.addObserver(this);

    // Set the screen and widget sizes
    _setSizes();
  }

  @override
  void dispose() {
    super.dispose();

    // Remove observer for screen size changes
    WidgetsBinding.instance?.removeObserver(this);
  }

  @override
  void didChangeMetrics() {
    setState(() {
      _setSizes();
    });
  }

  // Set the height and width dimensions
  _setSizes() {
    // Screen size values
    _screenHeight = WidgetsBinding.instance!.window.physicalSize.height;
    _screenWidth = WidgetsBinding.instance!.window.physicalSize.width;

    // Responsive_sizer widget size values
    _widgetHeight = 100.h;
    _widgetWidth = 100.w;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('Screen Actuals:'),
            Text(_screenHeight.toInt().toString()),
            Text(_screenWidth.toInt().toString()),
            const Text('Responsize Sizer Values:'),
            Text(_widgetHeight.toInt().toString()),
            Text(_widgetWidth.toInt().toString()),
          ],
        ),
      ),
    );
  }
}

You can recreate the issue by starting with a small window on screen, then maximising the window, restoring it down again, and finally resizing the window by dragging one edge, and noting the screen and widget sizes in each case.

Following this approach, I get the following dimensions output on screen:

A B C D E
  Screen height Screen width Widget height Widget width
Initial State 392 625 315 500
After Maximise 941 1920 314 500
After Restore Down 392 625 753 1536
After Drag Resize 402 630 321 504

In the initial state, the responsive_sizer values (columns D and E in the table) are proportional to the overall screen size.

After maximising the window, the responsive_sizer values are more or less unchanged. I assume that these should be about 80% of the screen height and width (as per the initial state values).

After restoring the window down to the initial size, the responsive_sizer values are updated to the values they should have been when the window was maximised.

Finally, if I select an edge of the window and drag it slightly, the responsive sizer values are set to the correct values for the current window size.

It appears as though the maximise and restore down functions for the window are not being responded to correctly by responsive_sizer.

UPDATE
I have tried this with the responsize_sizer example and that works perfectly with maximise and restore down, so I am assuming that it is my code that is the problem and not the responsive_sizer package. Any suggestions about what I am doing wrong? My aim was to set all my sizes in once place rather than having lots of "if web then size as this, else if tablet then size as this, else size as this" statements where the various sizes are used. Is that what I need to do?

I have changed my code to use callbacks to set the sizes on the fly as intended and removed the WidgetsBindingObserver and now everything works fine. So, the root cause was user error. Updated test programme below for anyone who finds themselves in a similar position.

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:responsive_sizer/responsive_sizer.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Web Window Resize Test',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: ResponsiveSizer(
        builder: (context, orientation, screenType) {
          return MyHomePage(title: 'Web Window Resize Test');
        },
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  //} with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
  }

  double getScreenHeight() {
    return WidgetsBinding.instance!.window.physicalSize.height;
  }

  double getScreenWidth() {
    return WidgetsBinding.instance!.window.physicalSize.width;
  }

  double getWidgetHeight() {
    // Logic for web, phone or tablet goes here
    double _height;
    if (kIsWeb) {
      // Sizes for web pages
      _height = 100.h;
    } else {
      if (Device.screenType == ScreenType.tablet) {
        // Sizes for tablets
        _height = 100.h;
      } else {
        // Sizes for mobile phones
        _height = 100.h;
      }
    }
    return _height;
  }

  double getWidgetWidth() {
    // Logic for web, phone or tablet goes here
    double _width;
    if (kIsWeb) {
      // Sizes for web pages
      _width = 100.w;
    } else {
      if (Device.screenType == ScreenType.tablet) {
        // Sizes for tablets
        _width = 100.w;
      } else {
        // Sizes for mobile phones
        _width = 100.w;
      }
    }
    return _width;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('Screen Actuals:'),
            Text(getScreenHeight().toInt().toString()),
            Text(getScreenWidth().toInt().toString()),
            const Text('Responsize Sizer Values:'),
            Text(getWidgetHeight().toInt().toString()),
            Text(getWidgetWidth().toInt().toString()),
          ],
        ),
      ),
    );
  }
}