Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 152 additions & 5 deletions feedback/example/lib/custom_feedback.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import 'dart:developer';
import 'dart:typed_data';

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

Expand Down Expand Up @@ -54,10 +57,12 @@ class CustomFeedbackForm extends StatefulWidget {
super.key,
required this.onSubmit,
required this.scrollController,
required this.screenshotController,
});

final OnSubmit onSubmit;
final ScrollController? scrollController;
final ScreenshotController? screenshotController;

@override
State<CustomFeedbackForm> createState() => _CustomFeedbackFormState();
Expand All @@ -66,6 +71,9 @@ class CustomFeedbackForm extends StatefulWidget {
class _CustomFeedbackFormState extends State<CustomFeedbackForm> {
final CustomFeedback _customFeedback = CustomFeedback();

Uint8List? _screenshotBytes;
bool _isCapturing = false;

@override
Widget build(BuildContext context) {
return Column(
Expand Down Expand Up @@ -141,20 +149,95 @@ class _CustomFeedbackFormState extends State<CustomFeedbackForm> {
mainAxisAlignment: MainAxisAlignment.center,
children: FeedbackRating.values.map(_ratingToIcon).toList(),
),

// ---------- Screenshot preview area ----------
const SizedBox(height: 16),
if (_screenshotBytes != null) ...[
Text('Screenshot preview:',
style: Theme.of(context).textTheme.bodyMedium),
const SizedBox(height: 8),
GestureDetector(
onTap: () => _openPreviewDialog(context),
child: Container(
constraints: BoxConstraints(
maxHeight: 220,
maxWidth: double.infinity,
),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.shade300),
borderRadius: BorderRadius.circular(8),
),
clipBehavior: Clip.hardEdge,
child: Image.memory(
_screenshotBytes!,
fit: BoxFit.contain,
gaplessPlayback: true,
),
),
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton.icon(
onPressed: _isCapturing ? null : _retakeScreenshot,
icon: const Icon(Icons.refresh),
label: const Text('Retake'),
),
const SizedBox(width: 12),
TextButton.icon(
onPressed: _isCapturing ? null : _clearScreenshot,
icon: const Icon(Icons.delete_outline),
label: const Text('Remove'),
),
],
),
],
// ---------- end preview ----------
],
),
],
),
),

// Capture button
Padding(
padding: const EdgeInsets.symmetric(vertical: 6.0),
child: TextButton(
onPressed: _isCapturing ? null : _handleTakeScreenshot,
child: _isCapturing
? Row(
mainAxisSize: MainAxisSize.min,
children: const [
SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(strokeWidth: 2)),
SizedBox(width: 8),
Text('Capturing...'),
],
)
: const Text('Take screenshot'),
),
),

// Submit button
TextButton(
// disable this button until the user has specified a feedback type
onPressed: _customFeedback.feedbackType != null
? () => widget.onSubmit(
? () async {
// Prepare extras: merge custom feedback map and optionally screenshot
final extras =
Map<String, dynamic>.from(_customFeedback.toMap());
if (_screenshotBytes != null) {
extras['screenshot'] = _screenshotBytes;
}
await widget.onSubmit(
_customFeedback.feedbackText ?? '',
extras: _customFeedback.toMap(),
)
extras: extras,
);
}
: null,
child: const Text('submit'),
child: const Text('Submit'),
),
const SizedBox(height: 8),
],
Expand Down Expand Up @@ -182,4 +265,68 @@ class _CustomFeedbackFormState extends State<CustomFeedbackForm> {
iconSize: 36,
);
}

Future<void> _handleTakeScreenshot() async {
if (widget.screenshotController == null) {
_showSnack('Screenshot controller is not available.');
return;
}

setState(() {
_isCapturing = true;
});

try {
// Capture with sensible default pixelRatio; you can adjust this
final bytes = await widget.screenshotController!.capture(pixelRatio: 2.0);
if (!mounted) return;
setState(() {
_screenshotBytes = bytes;
});
log('Screenshot captured: ${bytes.lengthInBytes} bytes');
} catch (e, st) {
log('Error capturing screenshot: $e\n$st');
_showSnack('Could not capture screenshot.');
} finally {
if (mounted) {
setState(() {
_isCapturing = false;
});
}
}
}

Future<void> _retakeScreenshot() async {
// small convenience wrapper to retake
await _handleTakeScreenshot();
}

void _clearScreenshot() {
setState(() {
_screenshotBytes = null;
});
}

void _openPreviewDialog(BuildContext context) {
print('object');
if (_screenshotBytes == null) return;
showDialog<void>(
context: context,
builder: (ctx) {
return Dialog(
insetPadding: const EdgeInsets.all(12),
child: InteractiveViewer(
child: Image.memory(_screenshotBytes!),
),
);
},
);
}

void _showSnack(String text) {
final messenger = ScaffoldMessenger.maybeOf(context);
if (messenger != null) {
messenger.showSnackBar(SnackBar(content: Text(text)));
}
}
}
4 changes: 3 additions & 1 deletion feedback/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,11 @@ class _MyAppState extends State<MyApp> {
// If custom feedback is not enabled, supply null and the default text
// feedback form will be used.
feedbackBuilder: _useCustomFeedback
? (context, onSubmit, scrollController) => CustomFeedbackForm(
? (context, onSubmit, scrollController, screenshotController) =>
CustomFeedbackForm(
onSubmit: onSubmit,
scrollController: scrollController,
screenshotController: screenshotController,
)
: null,
theme: FeedbackThemeData(
Expand Down
1 change: 1 addition & 0 deletions feedback/lib/feedback.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ library feedback;
export 'src/better_feedback.dart';
export 'src/feedback_controller.dart';
export 'src/feedback_mode.dart';
export 'src/screenshot.dart';
export 'src/l18n/translation.dart';
export 'src/theme/feedback_theme.dart' show FeedbackThemeData;
export 'src/user_feedback.dart';
1 change: 1 addition & 0 deletions feedback/lib/src/better_feedback.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ typedef FeedbackBuilder = Widget Function(
BuildContext,
OnSubmit,
ScrollController?,
ScreenshotController?,
);

/// A drag handle to be placed at the top of a draggable feedback sheet.
Expand Down
8 changes: 8 additions & 0 deletions feedback/lib/src/feedback_bottom_sheet.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// ignore_for_file: public_member_api_docs

import 'package:feedback/feedback.dart';
import 'package:feedback/src/better_feedback.dart';
import 'package:feedback/src/theme/feedback_theme.dart';
import 'package:feedback/src/utilities/back_button_interceptor.dart';
Expand All @@ -12,11 +13,13 @@ class FeedbackBottomSheet extends StatelessWidget {
required this.feedbackBuilder,
required this.onSubmit,
required this.sheetProgress,
required this.screenshotController,
});

final FeedbackBuilder feedbackBuilder;
final OnSubmit onSubmit;
final ValueNotifier<double> sheetProgress;
final ScreenshotController? screenshotController;

@override
Widget build(BuildContext context) {
Expand All @@ -26,6 +29,7 @@ class FeedbackBottomSheet extends StatelessWidget {
feedbackBuilder: feedbackBuilder,
onSubmit: onSubmit,
sheetProgress: sheetProgress,
screenshotController: screenshotController,
),
);
}
Expand All @@ -45,6 +49,7 @@ class FeedbackBottomSheet extends StatelessWidget {
context,
onSubmit,
null,
screenshotController,
),
);
},
Expand All @@ -60,11 +65,13 @@ class _DraggableFeedbackSheet extends StatefulWidget {
required this.feedbackBuilder,
required this.onSubmit,
required this.sheetProgress,
required this.screenshotController,
});

final FeedbackBuilder feedbackBuilder;
final OnSubmit onSubmit;
final ValueNotifier<double> sheetProgress;
final ScreenshotController? screenshotController;

@override
State<_DraggableFeedbackSheet> createState() =>
Expand Down Expand Up @@ -138,6 +145,7 @@ class _DraggableFeedbackSheetState extends State<_DraggableFeedbackSheet> {
context,
widget.onSubmit,
scrollController,
widget.screenshotController,
),
);
},
Expand Down
12 changes: 9 additions & 3 deletions feedback/lib/src/feedback_builder/string_feedback.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'package:feedback/src/better_feedback.dart';
import 'package:feedback/src/l18n/translation.dart';
import 'package:feedback/feedback.dart';
import 'package:feedback/src/theme/feedback_theme.dart';
import 'package:flutter/material.dart';

Expand All @@ -8,8 +7,13 @@ Widget simpleFeedbackBuilder(
BuildContext context,
OnSubmit onSubmit,
ScrollController? scrollController,
ScreenshotController? screenshotController,
) =>
StringFeedback(onSubmit: onSubmit, scrollController: scrollController);
StringFeedback(
onSubmit: onSubmit,
scrollController: scrollController,
screenshotController: screenshotController,
);

/// A form that prompts the user for feedback with a single text field.
/// This is the default feedback widget used by [BetterFeedback].
Expand All @@ -20,6 +24,7 @@ class StringFeedback extends StatefulWidget {
super.key,
required this.onSubmit,
required this.scrollController,
this.screenshotController,
});

/// Should be called when the user taps the submit button.
Expand All @@ -31,6 +36,7 @@ class StringFeedback extends StatefulWidget {
/// Non null if the sheet is draggable.
/// See: [FeedbackThemeData.sheetIsDraggable].
final ScrollController? scrollController;
final ScreenshotController? screenshotController;

@override
State<StringFeedback> createState() => _StringFeedbackState();
Expand Down
2 changes: 1 addition & 1 deletion feedback/lib/src/feedback_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -275,9 +275,9 @@ class FeedbackWidgetState extends State<FeedbackWidget>
widget.pixelRatio,
extras: extras,
);
painterController.clear();
},
sheetProgress: sheetProgress,
screenshotController: screenshotController,
),
),
),
Expand Down
2 changes: 1 addition & 1 deletion feedback/test/feedback_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ void main() {
submittedFeedback = feedback;
},
),
feedbackBuilder: (context, onSubmit, controller) {
feedbackBuilder: (context, onSubmit, controller, screenshotController) {
return SingleChildScrollView(
controller: controller,
child: TextButton(
Expand Down