Compare commits
6 Commits
d4d6e41a9d
...
d94e9eeb3d
| Author | SHA1 | Date | |
|---|---|---|---|
| d94e9eeb3d | |||
| d1b5cb54f4 | |||
| b36011d9e8 | |||
| 421171f1a3 | |||
| 3b12be497e | |||
| f615ed5654 |
@@ -96,18 +96,32 @@ class _DrawingBoardState extends State<DrawingBoard> {
|
||||
}
|
||||
|
||||
// Drawing mode: wrap with InteractiveViewer for zoom/pan
|
||||
return Align(
|
||||
alignment: widget.alignment,
|
||||
child: InteractiveViewer(
|
||||
transformationController: _transformationController,
|
||||
minScale: widget.minScale,
|
||||
maxScale: widget.maxScale,
|
||||
boundaryMargin: EdgeInsets.zero,
|
||||
constrained: true,
|
||||
panEnabled: !_isDrawing,
|
||||
scaleEnabled: !_isDrawing,
|
||||
child: content,
|
||||
),
|
||||
// Use LayoutBuilder to get available size and center content manually
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
// Calculate padding to center the content
|
||||
final horizontalPadding =
|
||||
(constraints.maxWidth - widget.boardSize.width) / 2;
|
||||
final verticalPadding =
|
||||
(constraints.maxHeight - widget.boardSize.height) / 2;
|
||||
|
||||
return InteractiveViewer(
|
||||
transformationController: _transformationController,
|
||||
minScale: widget.minScale,
|
||||
maxScale: widget.maxScale,
|
||||
boundaryMargin: const EdgeInsets.all(double.infinity),
|
||||
constrained: false,
|
||||
panEnabled: !_isDrawing,
|
||||
scaleEnabled: !_isDrawing,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: horizontalPadding > 0 ? horizontalPadding : 0,
|
||||
top: verticalPadding > 0 ? verticalPadding : 0,
|
||||
),
|
||||
child: content,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,10 +55,7 @@ class DrawingPainter extends CustomPainter {
|
||||
for (int i = 1; i < line.points.length - 1; i++) {
|
||||
final p0 = _toCanvasPoint(line.points[i]);
|
||||
final p1 = _toCanvasPoint(line.points[i + 1]);
|
||||
final midPoint = Offset(
|
||||
(p0.dx + p1.dx) / 2,
|
||||
(p0.dy + p1.dy) / 2,
|
||||
);
|
||||
final midPoint = Offset((p0.dx + p1.dx) / 2, (p0.dy + p1.dy) / 2);
|
||||
path.quadraticBezierTo(p0.dx, p0.dy, midPoint.dx, midPoint.dy);
|
||||
}
|
||||
// Draw to the last point
|
||||
@@ -116,61 +113,3 @@ class DrawingOverlay extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A widget that handles drawing input and renders lines.
|
||||
///
|
||||
/// Converts touch/pointer events to normalized coordinates and
|
||||
/// passes them to the [DrawingController].
|
||||
class DrawingCanvas extends StatelessWidget {
|
||||
final DrawingController controller;
|
||||
final Size canvasSize;
|
||||
final bool enabled;
|
||||
|
||||
const DrawingCanvas({
|
||||
super.key,
|
||||
required this.controller,
|
||||
required this.canvasSize,
|
||||
this.enabled = true,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Listener(
|
||||
onPointerDown: enabled ? _onPointerDown : null,
|
||||
onPointerMove: enabled ? _onPointerMove : null,
|
||||
onPointerUp: enabled ? _onPointerUp : null,
|
||||
onPointerCancel: enabled ? _onPointerCancel : null,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: DrawingOverlay(
|
||||
controller: controller,
|
||||
canvasSize: canvasSize,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onPointerDown(PointerDownEvent event) {
|
||||
final normalized = _toNormalized(event.localPosition);
|
||||
controller.startLine(normalized);
|
||||
}
|
||||
|
||||
void _onPointerMove(PointerMoveEvent event) {
|
||||
final normalized = _toNormalized(event.localPosition);
|
||||
controller.addPoint(normalized);
|
||||
}
|
||||
|
||||
void _onPointerUp(PointerUpEvent event) {
|
||||
controller.endLine();
|
||||
}
|
||||
|
||||
void _onPointerCancel(PointerCancelEvent event) {
|
||||
controller.endLine();
|
||||
}
|
||||
|
||||
/// Converts canvas coordinates to normalized coordinates (0-1).
|
||||
Offset _toNormalized(Offset canvasPoint) {
|
||||
return Offset(
|
||||
(canvasPoint.dx / canvasSize.width).clamp(0.0, 1.0),
|
||||
(canvasPoint.dy / canvasSize.height).clamp(0.0, 1.0),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,23 @@ import 'package:flutter/material.dart';
|
||||
import 'drawing_line.dart';
|
||||
import 'paint_preset.dart';
|
||||
|
||||
/// Represents an undoable action.
|
||||
sealed class DrawingAction {
|
||||
const DrawingAction();
|
||||
}
|
||||
|
||||
/// Action for adding a line.
|
||||
class AddLineAction extends DrawingAction {
|
||||
final DrawingLine line;
|
||||
const AddLineAction(this.line);
|
||||
}
|
||||
|
||||
/// Action for erasing lines.
|
||||
class EraseAction extends DrawingAction {
|
||||
final List<DrawingLine> erasedLines;
|
||||
const EraseAction(this.erasedLines);
|
||||
}
|
||||
|
||||
/// Controller for managing drawing state with undo/redo support.
|
||||
///
|
||||
/// Manages a stack of [DrawingLine] objects and provides methods for
|
||||
@@ -13,8 +30,11 @@ class DrawingController extends ChangeNotifier {
|
||||
/// All completed lines in the drawing
|
||||
final List<DrawingLine> _lines = [];
|
||||
|
||||
/// Lines that have been undone (for redo functionality)
|
||||
final List<DrawingLine> _undoneLines = [];
|
||||
/// Stack of actions for undo functionality
|
||||
final List<DrawingAction> _undoStack = [];
|
||||
|
||||
/// Stack of actions for redo functionality
|
||||
final List<DrawingAction> _redoStack = [];
|
||||
|
||||
/// The line currently being drawn (null when not drawing)
|
||||
DrawingLine? _currentLine;
|
||||
@@ -22,6 +42,12 @@ class DrawingController extends ChangeNotifier {
|
||||
/// Current paint preset being used
|
||||
PaintPreset _currentPreset = PaintPreset.blackPen;
|
||||
|
||||
/// Whether eraser mode is active
|
||||
bool _isEraserMode = false;
|
||||
|
||||
/// Lines erased in the current eraser stroke (for undo as single action)
|
||||
final List<DrawingLine> _currentErasedLines = [];
|
||||
|
||||
/// Maximum number of history steps to keep
|
||||
final int maxHistorySteps;
|
||||
|
||||
@@ -40,11 +66,14 @@ class DrawingController extends ChangeNotifier {
|
||||
/// Current paint preset
|
||||
PaintPreset get currentPreset => _currentPreset;
|
||||
|
||||
/// Whether eraser mode is active
|
||||
bool get isEraserMode => _isEraserMode;
|
||||
|
||||
/// Whether undo is available
|
||||
bool get canUndo => _lines.isNotEmpty;
|
||||
bool get canUndo => _undoStack.isNotEmpty;
|
||||
|
||||
/// Whether redo is available
|
||||
bool get canRedo => _undoneLines.isNotEmpty;
|
||||
bool get canRedo => _redoStack.isNotEmpty;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Drawing Operations
|
||||
@@ -52,6 +81,12 @@ class DrawingController extends ChangeNotifier {
|
||||
|
||||
/// Starts a new line at the given normalized position.
|
||||
void startLine(Offset normalizedPoint) {
|
||||
if (_isEraserMode) {
|
||||
_currentErasedLines.clear();
|
||||
_eraseAtPoint(normalizedPoint);
|
||||
return;
|
||||
}
|
||||
|
||||
_currentLine = DrawingLine(
|
||||
points: [normalizedPoint],
|
||||
color: _currentPreset.color,
|
||||
@@ -62,21 +97,41 @@ class DrawingController extends ChangeNotifier {
|
||||
|
||||
/// Adds a point to the current line.
|
||||
void addPoint(Offset normalizedPoint) {
|
||||
if (_isEraserMode) {
|
||||
_eraseAtPoint(normalizedPoint);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentLine == null) return;
|
||||
|
||||
// Filter points that are too close to reduce memory usage
|
||||
if (_currentLine!.isPointTooClose(normalizedPoint)) return;
|
||||
|
||||
_currentLine = _currentLine!.addPoint(normalizedPoint);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Completes the current line and adds it to the history.
|
||||
void endLine() {
|
||||
if (_isEraserMode) {
|
||||
// If we erased lines in this stroke, record as single action
|
||||
if (_currentErasedLines.isNotEmpty) {
|
||||
_undoStack.add(EraseAction(List.from(_currentErasedLines)));
|
||||
_redoStack.clear();
|
||||
_trimHistory();
|
||||
_currentErasedLines.clear();
|
||||
notifyListeners(); // Update UI to enable undo button
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentLine == null) return;
|
||||
|
||||
// Only add lines with at least 2 points
|
||||
if (_currentLine!.points.length >= 2) {
|
||||
_lines.add(_currentLine!);
|
||||
// Clear redo stack when new action is performed
|
||||
_undoneLines.clear();
|
||||
_undoStack.add(AddLineAction(_currentLine!));
|
||||
_redoStack.clear();
|
||||
_trimHistory();
|
||||
}
|
||||
|
||||
@@ -86,8 +141,98 @@ class DrawingController extends ChangeNotifier {
|
||||
|
||||
/// Trims history to maxHistorySteps to prevent memory growth.
|
||||
void _trimHistory() {
|
||||
while (_lines.length > maxHistorySteps) {
|
||||
_lines.removeAt(0);
|
||||
while (_undoStack.length > maxHistorySteps) {
|
||||
_undoStack.removeAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Eraser Operations
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Eraser hit radius in normalized coordinates
|
||||
static const double _eraserRadius = 0.015;
|
||||
|
||||
/// Checks if a point is near a line and erases it if so.
|
||||
void _eraseAtPoint(Offset point) {
|
||||
// Find all lines that intersect with the eraser point
|
||||
final linesToRemove = <DrawingLine>[];
|
||||
|
||||
for (final line in _lines) {
|
||||
if (_lineIntersectsPoint(
|
||||
line, point, _eraserRadius + line.strokeWidth / 2)) {
|
||||
linesToRemove.add(line);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove intersecting lines and track them for undo
|
||||
for (final line in linesToRemove) {
|
||||
_lines.remove(line);
|
||||
_currentErasedLines.add(line);
|
||||
}
|
||||
|
||||
if (linesToRemove.isNotEmpty) {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a line intersects with a circular point.
|
||||
bool _lineIntersectsPoint(DrawingLine line, Offset point, double radius) {
|
||||
// Check if any point is within radius
|
||||
for (final linePoint in line.points) {
|
||||
if ((linePoint - point).distance <= radius) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Check line segments
|
||||
for (int i = 0; i < line.points.length - 1; i++) {
|
||||
if (_pointToSegmentDistance(point, line.points[i], line.points[i + 1]) <=
|
||||
radius) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Calculates the distance from a point to a line segment.
|
||||
double _pointToSegmentDistance(Offset point, Offset segStart, Offset segEnd) {
|
||||
final dx = segEnd.dx - segStart.dx;
|
||||
final dy = segEnd.dy - segStart.dy;
|
||||
final lengthSquared = dx * dx + dy * dy;
|
||||
|
||||
if (lengthSquared == 0) {
|
||||
// Segment is a point
|
||||
return (point - segStart).distance;
|
||||
}
|
||||
|
||||
// Project point onto the line, clamping to segment
|
||||
var t = ((point.dx - segStart.dx) * dx + (point.dy - segStart.dy) * dy) /
|
||||
lengthSquared;
|
||||
t = t.clamp(0.0, 1.0);
|
||||
|
||||
final projection = Offset(
|
||||
segStart.dx + t * dx,
|
||||
segStart.dy + t * dy,
|
||||
);
|
||||
|
||||
return (point - projection).distance;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Eraser Mode
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Toggles eraser mode.
|
||||
void toggleEraserMode() {
|
||||
_isEraserMode = !_isEraserMode;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Sets eraser mode.
|
||||
void setEraserMode(bool enabled) {
|
||||
if (_isEraserMode != enabled) {
|
||||
_isEraserMode = enabled;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,29 +240,55 @@ class DrawingController extends ChangeNotifier {
|
||||
// Undo/Redo
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Undoes the last line drawn.
|
||||
/// Undoes the last action.
|
||||
void undo() {
|
||||
if (!canUndo) return;
|
||||
|
||||
final line = _lines.removeLast();
|
||||
_undoneLines.add(line);
|
||||
final action = _undoStack.removeLast();
|
||||
|
||||
switch (action) {
|
||||
case AddLineAction(:final line):
|
||||
// Remove the line that was added
|
||||
_lines.remove(line);
|
||||
_redoStack.add(action);
|
||||
case EraseAction(:final erasedLines):
|
||||
// Restore the lines that were erased
|
||||
_lines.addAll(erasedLines);
|
||||
_redoStack.add(action);
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Redoes the last undone line.
|
||||
/// Redoes the last undone action.
|
||||
void redo() {
|
||||
if (!canRedo) return;
|
||||
|
||||
final line = _undoneLines.removeLast();
|
||||
_lines.add(line);
|
||||
final action = _redoStack.removeLast();
|
||||
|
||||
switch (action) {
|
||||
case AddLineAction(:final line):
|
||||
// Re-add the line
|
||||
_lines.add(line);
|
||||
_undoStack.add(action);
|
||||
case EraseAction(:final erasedLines):
|
||||
// Re-erase the lines
|
||||
for (final line in erasedLines) {
|
||||
_lines.remove(line);
|
||||
}
|
||||
_undoStack.add(action);
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Clears all lines from the canvas.
|
||||
void clear() {
|
||||
_lines.clear();
|
||||
_undoneLines.clear();
|
||||
_undoStack.clear();
|
||||
_redoStack.clear();
|
||||
_currentLine = null;
|
||||
_currentErasedLines.clear();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@@ -125,9 +296,10 @@ class DrawingController extends ChangeNotifier {
|
||||
// Paint Preset
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Sets the current paint preset.
|
||||
/// Sets the current paint preset and disables eraser mode.
|
||||
void setPreset(PaintPreset preset) {
|
||||
_currentPreset = preset;
|
||||
_isEraserMode = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@@ -148,8 +320,10 @@ class DrawingController extends ChangeNotifier {
|
||||
/// Imports lines from a JSON-serializable list.
|
||||
void fromJsonList(List<Map<String, dynamic>> jsonList) {
|
||||
_lines.clear();
|
||||
_undoneLines.clear();
|
||||
_undoStack.clear();
|
||||
_redoStack.clear();
|
||||
_currentLine = null;
|
||||
_currentErasedLines.clear();
|
||||
|
||||
for (final json in jsonList) {
|
||||
_lines.add(DrawingLine.fromJson(json));
|
||||
@@ -183,7 +357,8 @@ class DrawingController extends ChangeNotifier {
|
||||
@override
|
||||
void dispose() {
|
||||
_lines.clear();
|
||||
_undoneLines.clear();
|
||||
_undoStack.clear();
|
||||
_redoStack.clear();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@ import 'dart:ui';
|
||||
///
|
||||
/// This allows drawings to scale correctly when the canvas size changes.
|
||||
class DrawingLine {
|
||||
/// The minimal squared distance between to points which are normalized so that this point is allowed to be added to the line
|
||||
static const minNormalizedPointDistanceSquared = 0.001 * 0.001;
|
||||
|
||||
/// Points in normalized coordinates (0.0 to 1.0)
|
||||
final List<Offset> points;
|
||||
|
||||
@@ -27,10 +30,9 @@ class DrawingLine {
|
||||
/// Creates a DrawingLine from JSON data.
|
||||
factory DrawingLine.fromJson(Map<String, dynamic> json) {
|
||||
final pointsList = (json['points'] as List)
|
||||
.map((p) => Offset(
|
||||
(p['x'] as num).toDouble(),
|
||||
(p['y'] as num).toDouble(),
|
||||
))
|
||||
.map(
|
||||
(p) => Offset((p['x'] as num).toDouble(), (p['y'] as num).toDouble()),
|
||||
)
|
||||
.toList();
|
||||
|
||||
return DrawingLine(
|
||||
@@ -58,6 +60,14 @@ class DrawingLine {
|
||||
);
|
||||
}
|
||||
|
||||
bool isPointTooClose(Offset nextNormalizedPoint) {
|
||||
if (points.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
return (points.last - nextNormalizedPoint).distanceSquared <
|
||||
minNormalizedPointDistanceSquared;
|
||||
}
|
||||
|
||||
/// Creates a copy with updated points.
|
||||
DrawingLine copyWith({
|
||||
List<Offset>? points,
|
||||
@@ -85,9 +95,5 @@ class DrawingLine {
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
Object.hashAll(points),
|
||||
color,
|
||||
strokeWidth,
|
||||
);
|
||||
int get hashCode => Object.hash(Object.hashAll(points), color, strokeWidth);
|
||||
}
|
||||
|
||||
@@ -43,13 +43,24 @@ class DrawingToolbar extends StatelessWidget {
|
||||
...PaintPreset.quickAccess.map((preset) => _buildPresetButton(
|
||||
context,
|
||||
preset,
|
||||
isSelected: controller.currentPreset == preset,
|
||||
isSelected: !controller.isEraserMode &&
|
||||
controller.currentPreset == preset,
|
||||
)),
|
||||
|
||||
const SizedBox(width: 8),
|
||||
_buildDivider(context),
|
||||
const SizedBox(width: 8),
|
||||
|
||||
// Eraser button
|
||||
_buildEraserButton(
|
||||
context,
|
||||
isSelected: controller.isEraserMode,
|
||||
),
|
||||
|
||||
const SizedBox(width: 8),
|
||||
_buildDivider(context),
|
||||
const SizedBox(width: 8),
|
||||
|
||||
// Undo button
|
||||
_buildActionButton(
|
||||
context,
|
||||
@@ -127,6 +138,37 @@ class DrawingToolbar extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEraserButton(
|
||||
BuildContext context, {
|
||||
required bool isSelected,
|
||||
}) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Tooltip(
|
||||
message: 'Eraser',
|
||||
child: InkWell(
|
||||
onTap: () => controller.setEraserMode(true),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Container(
|
||||
width: 36,
|
||||
height: 36,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 2),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: isSelected
|
||||
? Border.all(color: colorScheme.primary, width: 2)
|
||||
: null,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.auto_fix_high,
|
||||
size: 20,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionButton(
|
||||
BuildContext context, {
|
||||
required IconData icon,
|
||||
|
||||
@@ -57,7 +57,7 @@ class PaintPreset {
|
||||
static const yellowMarker = PaintPreset(
|
||||
name: 'Yellow Marker',
|
||||
color: Color(0x80FFEB3B), // Yellow with 50% opacity
|
||||
strokeWidth: 0.015, // Thicker for highlighting
|
||||
strokeWidth: 0.02, // Thicker for highlighting
|
||||
icon: Icons.highlight,
|
||||
);
|
||||
|
||||
@@ -65,7 +65,7 @@ class PaintPreset {
|
||||
static const greenMarker = PaintPreset(
|
||||
name: 'Green Marker',
|
||||
color: Color(0x804CAF50), // Green with 50% opacity
|
||||
strokeWidth: 0.015,
|
||||
strokeWidth: 0.018,
|
||||
icon: Icons.highlight,
|
||||
);
|
||||
|
||||
@@ -91,7 +91,10 @@ class PaintPreset {
|
||||
static const List<PaintPreset> quickAccess = [
|
||||
blackPen,
|
||||
redPen,
|
||||
bluePen,
|
||||
yellowMarker,
|
||||
greenMarker,
|
||||
pinkMarker,
|
||||
];
|
||||
|
||||
@override
|
||||
|
||||
@@ -271,8 +271,9 @@ class _SheetViewerPageState extends State<SheetViewerPage>
|
||||
icon: Icon(
|
||||
widget.config.fullscreen ? Icons.fullscreen_exit : Icons.fullscreen,
|
||||
),
|
||||
tooltip:
|
||||
widget.config.fullscreen ? 'Exit Fullscreen' : 'Enter Fullscreen',
|
||||
tooltip: widget.config.fullscreen
|
||||
? 'Exit Fullscreen'
|
||||
: 'Enter Fullscreen',
|
||||
onPressed: _toggleFullscreen,
|
||||
),
|
||||
IconButton(
|
||||
@@ -284,8 +285,9 @@ class _SheetViewerPageState extends State<SheetViewerPage>
|
||||
icon: Icon(
|
||||
widget.config.twoPageMode ? Icons.filter_1 : Icons.filter_2,
|
||||
),
|
||||
tooltip:
|
||||
widget.config.twoPageMode ? 'Single Page Mode' : 'Two Page Mode',
|
||||
tooltip: widget.config.twoPageMode
|
||||
? 'Single Page Mode'
|
||||
: 'Two Page Mode',
|
||||
onPressed: _toggleTwoPageMode,
|
||||
),
|
||||
],
|
||||
@@ -317,8 +319,9 @@ class _SheetViewerPageState extends State<SheetViewerPage>
|
||||
currentPageNumber: _currentPage,
|
||||
config: widget.config,
|
||||
leftDrawingController: _leftDrawingController,
|
||||
rightDrawingController:
|
||||
widget.config.twoPageMode ? _rightDrawingController : null,
|
||||
rightDrawingController: widget.config.twoPageMode
|
||||
? _rightDrawingController
|
||||
: null,
|
||||
drawingEnabled: _isPaintMode,
|
||||
);
|
||||
|
||||
@@ -334,7 +337,6 @@ class _SheetViewerPageState extends State<SheetViewerPage>
|
||||
onToggleFullscreen: _toggleFullscreen,
|
||||
onExit: () => Navigator.pop(context),
|
||||
onPageTurn: _turnPage,
|
||||
child: pageDisplay,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -356,10 +358,9 @@ class _SheetViewerPageState extends State<SheetViewerPage>
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
message,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleMedium
|
||||
?.copyWith(color: Colors.red),
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleMedium?.copyWith(color: Colors.red),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -14,7 +14,6 @@ typedef PageTurnCallback = dynamic Function(int delta);
|
||||
/// - Right side: Turn page forward (+1 or +2 in two-page mode)
|
||||
class TouchNavigationLayer extends StatelessWidget {
|
||||
final PdfPageDisplay pageDisplay;
|
||||
final Widget child;
|
||||
final Config config;
|
||||
final VoidCallback onToggleFullscreen;
|
||||
final VoidCallback onExit;
|
||||
@@ -23,7 +22,6 @@ class TouchNavigationLayer extends StatelessWidget {
|
||||
const TouchNavigationLayer({
|
||||
super.key,
|
||||
required this.pageDisplay,
|
||||
required this.child,
|
||||
required this.config,
|
||||
required this.onToggleFullscreen,
|
||||
required this.onExit,
|
||||
@@ -35,7 +33,7 @@ class TouchNavigationLayer extends StatelessWidget {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTapUp: (details) => _handleTap(context, details),
|
||||
child: child,
|
||||
child: pageDisplay,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user