Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI Issues #871

Open
calcitem opened this issue Oct 5, 2024 · 0 comments
Open

UI Issues #871

calcitem opened this issue Oct 5, 2024 · 0 comments
Labels
bug Something isn't working

Comments

@calcitem
Copy link
Owner

calcitem commented Oct 5, 2024

2. Images Not Reloading on Setting Changes

Problem Description:

In game_board.dart, images are loaded only once in initState() through _loadImages() and stored in gameImagesFuture. If the user changes the display settings (e.g., changing the board or piece images), the UI will not update to reflect the new settings because the images are not reloaded.

Solution:

Listen for changes in display settings and reload the images when relevant settings change. You can use ValueListenableBuilder in the build() method to listen for changes in display settings and call setState() to trigger a reload when settings change.

Modified code example:

@override
Widget build(BuildContext context) {
  return ValueListenableBuilder<Box<DisplaySettings>>(
    valueListenable: DB().listenDisplaySettings,
    builder: (BuildContext context, Box<DisplaySettings> box, _) {
      gameImagesFuture = _loadImages(); // Reload images when settings change

      // The rest of the code remains the same
    },
  );
}

3. Incorrect Return Value in shouldRepaint Method of CustomPainter

Problem Description:

In the BoardPainter class in board_painter.dart, the shouldRepaint method always returns false:

@override
bool shouldRepaint(CustomPainter oldDelegate) => false;

This means that even if the properties of BoardPainter change, Flutter will not redraw, potentially causing the UI to fail to update in a timely manner.

Solution:

In the shouldRepaint method, compare the old and new BoardPainter instances to determine if a repaint is needed. For example, it can be rewritten as follows:

@override
bool shouldRepaint(BoardPainter oldDelegate) {
  return oldDelegate.backgroundImage != backgroundImage ||
         oldDelegate.someOtherProperty != someOtherProperty;
}

By accurately determining when a repaint is needed, you can improve performance and ensure UI correctness.

4. Performance Optimization for Image Loading

Problem Description:

Loading images through rootBundle.load and decoding them with ui.instantiateImageCodec each time may lead to performance issues, especially in cases of frequent rebuilds.

Solution:

Consider caching loaded images to avoid repeated decoding. You can use the caching mechanism provided by Image.asset or implement image caching at the application level. For example:

final Map<String, ui.Image> _imageCache = {};

Future<ui.Image> loadImage(String assetPath) async {
  if (_imageCache.containsKey(assetPath)) {
    return _imageCache[assetPath]!;
  }
  final ByteData data = await rootBundle.load(assetPath);
  final ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
  final ui.FrameInfo frame = await codec.getNextFrame();
  _imageCache[assetPath] = frame.image;
  return frame.image;
}

5. Handling Potential Issues with Asynchronous Operations

Problem Description:

In the processInitialSharingMoveList() method, there are multiple checks for mounted to prevent updating the UI when the Widget has been unmounted. However, if not handled properly between asynchronous operations, state updates may still occur for unmounted Widgets.

Solution:

Ensure that all asynchronous operations are followed by mounted checks or always check the mounted status when needing to update the UI. For example:

void processInitialSharingMoveList() async {
  if (!mounted || GameController().initialSharingMoveListNotifier.value == null) {
    return;
  }

  try {
    await ImportService.import(GameController().initialSharingMoveList!);
    if (!mounted) return;
    LoadService.handleHistoryNavigation(context);
  } catch (e) {
    if (!mounted) return;
    // Error handling code
  }

  if (mounted && GameController().loadedGameFilenamePrefix != null) {
    // Show prompt
  }

  if (mounted) {
    // Update UI
  }

  GameController().initialSharingMoveList = null;
}

6. Using Expired BuildContext in Asynchronous Callbacks

Problem Description:

In some asynchronous operations, an expired BuildContext may be captured, leading to exceptions in operations such as Navigator or Scaffold.

Solution:

In asynchronous operations, use if (!mounted) return; checks or ensure the correct context is used by methods like Navigator.of(context, rootNavigator: true).

Summary

The above are the main robustness and performance issues present in the code. It is recommended to make corresponding modifications to the code to improve the stability and performance of the application.

@calcitem calcitem added the bug Something isn't working label Oct 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant