-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Open
Labels
P2A bug or feature request we're likely to work onA bug or feature request we're likely to work onarea-devexpFor issues related to the analysis server, IDE support, linter, `dart fix`, and diagnostic messages.For issues related to the analysis server, IDE support, linter, `dart fix`, and diagnostic messages.devexp-refactoringIssues with analysis server refactoringsIssues with analysis server refactoringstype-bugIncorrect behavior (everything from a crash to more subtle misbehavior)Incorrect behavior (everything from a crash to more subtle misbehavior)
Description
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:lutme/adaptive_image.dart';
import 'package:lutme/ref_image_edited_before_lut_path_provider.dart';
import 'package:lutme/resolution.dart';
class RefImageEditedBeforeLutWidget extends ConsumerWidget {
const RefImageEditedBeforeLutWidget({super.key});
@override
Widget build(final BuildContext context, final WidgetRef ref) {
return LayoutBuilder(
builder: (final BuildContext context, final BoxConstraints constraints) {
// Calculate the maximum resolution based on the available space.
final Resolution maxResolution =
calculateMaxResolution(context, constraints, step: 200);
// Define arguments for the low-resolution image provider.
const lowRes = 512;
final lowResArg = RefImageEditedBeforeLutPathArg.fromProviders(ref,
resolution: const Resolution(lowRes, lowRes));
// --- Step 1: Watch the low-resolution provider exclusively first. ---
final lowResPathAsync =
ref.watch(refImageEditedBeforeLutPathProvider(lowResArg));
return lowResPathAsync.when(
loading: () {
// --- State: Initial loading state for low-res ---
return const Image(image: AssetImage("assets/imgs/Spinner.gif"));
},
error: (error, stackTrace) {
// --- State: Error handling for low-res ---
return Center(
child: Text(
"Error loading image: $error",
textAlign: TextAlign.center,
),
);
},
data: (lowResPath) {
if (lowResPath == null) {
return const SizedBox.shrink();
}
// --- Step 2: Low-res is available. Now watch the high-res provider. ---
final highResArg = RefImageEditedBeforeLutPathArg.fromProviders(ref,
resolution: maxResolution);
final highResPathAsync =
ref.watch(refImageEditedBeforeLutPathProvider(highResArg));
final highResPath = highResPathAsync.asData?.value;
// --- The Solution: Use a Stack to prevent flickering ---
// The low-resolution image acts as a persistent background,
// ensuring something is always on screen. The high-resolution
// image is then layered on top once it's fully loaded.
return Stack(
fit: StackFit.expand,
alignment: Alignment.center,
children: [
// Layer 1: The low-resolution image is always visible.
AdaptiveImageWithoutFadeIn(
FileImage(File(lowResPath)),
isPreview: false,
),
// Layer 2: The high-resolution image appears on top when ready.
// It's wrapped in an AnimatedOpacity for a smooth fade-in effect,
// which is visually more appealing than an instant pop-in.
AnimatedOpacity(
opacity: highResPath != null ? 1.0 : 0.0,
duration: const Duration(milliseconds: 300),
child: highResPath != null
? AdaptiveImageWithoutFadeIn(
FileImage(File(highResPath)),
isPreview: false,
)
: const SizedBox.shrink(),
),
// Layer 3: The loading indicator overlay.
// This appears on top of the low-res image while the high-res is loading.
if (highResPathAsync.isLoading)
const Center(
child: Tooltip(
message: "Loading higher quality preview",
child: CircularProgressIndicator(),
),
),
// If high-res failed, we can optionally show an indicator or just log it.
if (highResPathAsync.hasError)
// ignore: avoid_print
Builder(builder: (context) {
print("Failed to load high-res image, showing low-res instead: ${highResPathAsync.error}");
return const SizedBox.shrink();
},)
],
);
},
);
},
);
}
}
extract widget on the Stack
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:lutme/adaptive_image.dart';
import 'package:lutme/ref_image_edited_before_lut_path_provider.dart';
import 'package:lutme/resolution.dart';
class RefImageEditedBeforeLutWidget extends ConsumerWidget {
const RefImageEditedBeforeLutWidget({super.key});
@override
Widget build(final BuildContext context, final WidgetRef ref) {
return LayoutBuilder(
builder: (final BuildContext context, final BoxConstraints constraints) {
// Calculate the maximum resolution based on the available space.
final Resolution maxResolution =
calculateMaxResolution(context, constraints, step: 200);
// Define arguments for the low-resolution image provider.
const lowRes = 512;
final lowResArg = RefImageEditedBeforeLutPathArg.fromProviders(ref,
resolution: const Resolution(lowRes, lowRes));
// --- Step 1: Watch the low-resolution provider exclusively first. ---
final lowResPathAsync =
ref.watch(refImageEditedBeforeLutPathProvider(lowResArg));
return lowResPathAsync.when(
loading: () {
// --- State: Initial loading state for low-res ---
return const Image(image: AssetImage("assets/imgs/Spinner.gif"));
},
error: (error, stackTrace) {
// --- State: Error handling for low-res ---
return Center(
child: Text(
"Error loading image: $error",
textAlign: TextAlign.center,
),
);
},
data: (lowResPath) {
if (lowResPath == null) {
return const SizedBox.shrink();
}
// --- Step 2: Low-res is available. Now watch the high-res provider. ---
final highResArg = RefImageEditedBeforeLutPathArg.fromProviders(ref,
resolution: maxResolution);
final highResPathAsync =
ref.watch(refImageEditedBeforeLutPathProvider(highResArg));
final highResPath = highResPathAsync.asData?.value;
// --- The Solution: Use a Stack to prevent flickering ---
// The low-resolution image acts as a persistent background,
// ensuring something is always on screen. The high-resolution
// image is then layered on top once it's fully loaded.
return MyStack(highResPath: highResPath, highResPathAsync: highResPathAsync);
},
);
},
);
}
}
class MyStack extends StatelessWidget {
const MyStack({
super.key,
required this.highResPath,
required this.highResPathAsync,
});
final String? highResPath;
final AsyncValue<String?> highResPathAsync;
@override
Widget build(BuildContext context) {
return Stack(
fit: StackFit.expand,
alignment: Alignment.center,
children: [
// Layer 1: The low-resolution image is always visible.
AdaptiveImageWithoutFadeIn(
FileImage(File(lowResPath)),
isPreview: false,
),
// Layer 2: The high-resolution image appears on top when ready.
// It's wrapped in an AnimatedOpacity for a smooth fade-in effect,
// which is visually more appealing than an instant pop-in.
AnimatedOpacity(
opacity: highResPath != null ? 1.0 : 0.0,
duration: const Duration(milliseconds: 300),
child: highResPath != null
? AdaptiveImageWithoutFadeIn(
FileImage(File(highResPath)),
isPreview: false,
)
: const SizedBox.shrink(),
),
// Layer 3: The loading indicator overlay.
// This appears on top of the low-res image while the high-res is loading.
if (highResPathAsync.isLoading)
const Center(
child: Tooltip(
message: "Loading higher quality preview",
child: CircularProgressIndicator(),
),
),
// If high-res failed, we can optionally show an indicator or just log it.
if (highResPathAsync.hasError)
// ignore: avoid_print
Builder(builder: (context) {
print("Failed to load high-res image, showing low-res instead: ${highResPathAsync.error}");
return const SizedBox.shrink();
},)
],
);
}
}
Undefined name 'lowResPath'. Try correcting the name to one that is defined, or defining the name.
The argument type 'String?' can't be assigned to the parameter type 'String'.
Metadata
Metadata
Assignees
Labels
P2A bug or feature request we're likely to work onA bug or feature request we're likely to work onarea-devexpFor issues related to the analysis server, IDE support, linter, `dart fix`, and diagnostic messages.For issues related to the analysis server, IDE support, linter, `dart fix`, and diagnostic messages.devexp-refactoringIssues with analysis server refactoringsIssues with analysis server refactoringstype-bugIncorrect behavior (everything from a crash to more subtle misbehavior)Incorrect behavior (everything from a crash to more subtle misbehavior)