Showing
6 changed files
with
200 additions
and
28 deletions
@@ -24,6 +24,7 @@ export 'src/asset_utils.dart'; | @@ -24,6 +24,7 @@ export 'src/asset_utils.dart'; | ||
24 | export 'src/cache.dart'; | 24 | export 'src/cache.dart'; |
25 | export 'src/callback.dart'; | 25 | export 'src/callback.dart'; |
26 | export 'src/fonts/gfonts.dart'; | 26 | export 'src/fonts/gfonts.dart'; |
27 | +export 'src/preview/action_bar_theme.dart'; | ||
27 | export 'src/preview/actions.dart'; | 28 | export 'src/preview/actions.dart'; |
28 | export 'src/preview/pdf_preview.dart'; | 29 | export 'src/preview/pdf_preview.dart'; |
29 | export 'src/printer.dart'; | 30 | export 'src/printer.dart'; |
1 | +import 'package:flutter/foundation.dart'; | ||
2 | +import 'package:flutter/material.dart'; | ||
3 | + | ||
4 | +class PdfActionBarTheme with Diagnosticable { | ||
5 | + /// Creates a theme for action bar of [PdfPreviewController]. | ||
6 | + const PdfActionBarTheme({ | ||
7 | + this.backgroundColor, | ||
8 | + this.iconColor, | ||
9 | + this.height, | ||
10 | + this.textStyle, | ||
11 | + this.elevation = 4.0, | ||
12 | + this.actionSpacing = 0.0, | ||
13 | + this.alignment = WrapAlignment.spaceAround, | ||
14 | + this.runAlignment = WrapAlignment.center, | ||
15 | + this.crossAxisAlignment = WrapCrossAlignment.center, | ||
16 | + }); | ||
17 | + | ||
18 | + final Color? backgroundColor; | ||
19 | + final Color? iconColor; | ||
20 | + final double? height; | ||
21 | + final TextStyle? textStyle; | ||
22 | + final double elevation; | ||
23 | + final double actionSpacing; | ||
24 | + final WrapAlignment alignment; | ||
25 | + final WrapAlignment runAlignment; | ||
26 | + final WrapCrossAlignment crossAxisAlignment; | ||
27 | + | ||
28 | + /// Creates a copy of this object but with the given fields replaced with the | ||
29 | + /// new values. | ||
30 | + PdfActionBarTheme copyWith({ | ||
31 | + Color? backgroundColor, | ||
32 | + Color? iconColor, | ||
33 | + double? height, | ||
34 | + TextStyle? textStyle, | ||
35 | + double? elevation, | ||
36 | + double? actionSpacing, | ||
37 | + WrapAlignment? alignment, | ||
38 | + WrapAlignment? runAlignment, | ||
39 | + WrapCrossAlignment? crossAxisAlignment, | ||
40 | + }) { | ||
41 | + return PdfActionBarTheme( | ||
42 | + backgroundColor: backgroundColor ?? this.backgroundColor, | ||
43 | + iconColor: iconColor ?? this.iconColor, | ||
44 | + height: height ?? this.height, | ||
45 | + textStyle: textStyle ?? this.textStyle, | ||
46 | + elevation: elevation ?? this.elevation, | ||
47 | + actionSpacing: actionSpacing ?? this.actionSpacing, | ||
48 | + alignment: alignment ?? this.alignment, | ||
49 | + runAlignment: runAlignment ?? this.runAlignment, | ||
50 | + crossAxisAlignment: crossAxisAlignment ?? this.crossAxisAlignment, | ||
51 | + ); | ||
52 | + } | ||
53 | + | ||
54 | + @override | ||
55 | + int get hashCode => Object.hashAll([ | ||
56 | + backgroundColor, | ||
57 | + iconColor, | ||
58 | + height, | ||
59 | + textStyle, | ||
60 | + elevation, | ||
61 | + actionSpacing, | ||
62 | + alignment, | ||
63 | + runAlignment, | ||
64 | + crossAxisAlignment, | ||
65 | + ]); | ||
66 | + | ||
67 | + @override | ||
68 | + bool operator ==(Object other) { | ||
69 | + if (identical(this, other)) { | ||
70 | + return true; | ||
71 | + } | ||
72 | + if (other.runtimeType != runtimeType) { | ||
73 | + return false; | ||
74 | + } | ||
75 | + return other is PdfActionBarTheme && | ||
76 | + other.backgroundColor == backgroundColor && | ||
77 | + other.iconColor == iconColor && | ||
78 | + other.height == height && | ||
79 | + other.textStyle == textStyle && | ||
80 | + other.elevation == elevation && | ||
81 | + other.actionSpacing == actionSpacing && | ||
82 | + other.alignment == alignment && | ||
83 | + other.runAlignment == runAlignment && | ||
84 | + other.crossAxisAlignment == crossAxisAlignment; | ||
85 | + } | ||
86 | + | ||
87 | + @override | ||
88 | + void debugFillProperties(DiagnosticPropertiesBuilder properties) { | ||
89 | + super.debugFillProperties(properties); | ||
90 | + properties.add(ColorProperty('backgroundColor', backgroundColor)); | ||
91 | + properties.add(ColorProperty('iconColor', iconColor)); | ||
92 | + properties.add(DoubleProperty('height', height)); | ||
93 | + properties.add(DiagnosticsProperty<TextStyle>('textStyle', textStyle)); | ||
94 | + properties.add(DoubleProperty('elevation', elevation)); | ||
95 | + properties.add(DoubleProperty('actionSpacing', actionSpacing)); | ||
96 | + properties.add(DiagnosticsProperty<WrapAlignment>('alignment', alignment, | ||
97 | + defaultValue: WrapAlignment.spaceAround)); | ||
98 | + properties.add(DiagnosticsProperty<WrapAlignment>( | ||
99 | + 'runAlignment', runAlignment, | ||
100 | + defaultValue: WrapAlignment.center)); | ||
101 | + properties.add(DiagnosticsProperty<WrapCrossAlignment>( | ||
102 | + 'crossAxisAlignment', crossAxisAlignment, | ||
103 | + defaultValue: WrapCrossAlignment.center)); | ||
104 | + } | ||
105 | +} |
@@ -50,6 +50,7 @@ class PdfPreviewCustom extends StatefulWidget { | @@ -50,6 +50,7 @@ class PdfPreviewCustom extends StatefulWidget { | ||
50 | this.scrollPhysics, | 50 | this.scrollPhysics, |
51 | this.shrinkWrap = false, | 51 | this.shrinkWrap = false, |
52 | this.pagesBuilder, | 52 | this.pagesBuilder, |
53 | + this.enableScrollToPage = false, | ||
53 | }) : super(key: key); | 54 | }) : super(key: key); |
54 | 55 | ||
55 | /// Pdf paper page format | 56 | /// Pdf paper page format |
@@ -102,6 +103,9 @@ class PdfPreviewCustom extends StatefulWidget { | @@ -102,6 +103,9 @@ class PdfPreviewCustom extends StatefulWidget { | ||
102 | /// their own pages. | 103 | /// their own pages. |
103 | final CustomPdfPagesBuilder? pagesBuilder; | 104 | final CustomPdfPagesBuilder? pagesBuilder; |
104 | 105 | ||
106 | + /// Whether scroll to page functionality enabled. | ||
107 | + final bool enableScrollToPage; | ||
108 | + | ||
105 | @override | 109 | @override |
106 | PdfPreviewCustomState createState() => PdfPreviewCustomState(); | 110 | PdfPreviewCustomState createState() => PdfPreviewCustomState(); |
107 | } | 111 | } |
@@ -110,6 +114,8 @@ class PdfPreviewCustomState extends State<PdfPreviewCustom> | @@ -110,6 +114,8 @@ class PdfPreviewCustomState extends State<PdfPreviewCustom> | ||
110 | with PdfPreviewRaster { | 114 | with PdfPreviewRaster { |
111 | final listView = GlobalKey(); | 115 | final listView = GlobalKey(); |
112 | 116 | ||
117 | + List<GlobalKey> _pageGlobalKeys = <GlobalKey>[]; | ||
118 | + | ||
113 | bool infoLoaded = false; | 119 | bool infoLoaded = false; |
114 | 120 | ||
115 | int? preview; | 121 | int? preview; |
@@ -172,6 +178,24 @@ class PdfPreviewCustomState extends State<PdfPreviewCustom> | @@ -172,6 +178,24 @@ class PdfPreviewCustomState extends State<PdfPreviewCustom> | ||
172 | super.didChangeDependencies(); | 178 | super.didChangeDependencies(); |
173 | } | 179 | } |
174 | 180 | ||
181 | + /// Ensures that page with [index] is become visible. | ||
182 | + Future<void> scrollToPage( | ||
183 | + int index, { | ||
184 | + Duration duration = const Duration(milliseconds: 300), | ||
185 | + Curve curve = Curves.ease, | ||
186 | + ScrollPositionAlignmentPolicy alignmentPolicy = | ||
187 | + ScrollPositionAlignmentPolicy.explicit, | ||
188 | + }) { | ||
189 | + assert(index >= 0, 'Index of page cannot be negative'); | ||
190 | + final pageContext = _pageGlobalKeys[index].currentContext; | ||
191 | + assert(pageContext != null, 'Context of GlobalKey cannot be null'); | ||
192 | + return Scrollable.ensureVisible(pageContext!, | ||
193 | + duration: duration, curve: curve, alignmentPolicy: alignmentPolicy); | ||
194 | + } | ||
195 | + | ||
196 | + /// Returns the global key for page with [index]. | ||
197 | + Key getPageKey(int index) => _pageGlobalKeys[index]; | ||
198 | + | ||
175 | Widget _showError(Object error) { | 199 | Widget _showError(Object error) { |
176 | if (widget.onError != null) { | 200 | if (widget.onError != null) { |
177 | return widget.onError!(context, error); | 201 | return widget.onError!(context, error); |
@@ -197,30 +221,53 @@ class PdfPreviewCustomState extends State<PdfPreviewCustom> | @@ -197,30 +221,53 @@ class PdfPreviewCustomState extends State<PdfPreviewCustom> | ||
197 | ); | 221 | ); |
198 | } | 222 | } |
199 | 223 | ||
224 | + if (widget.enableScrollToPage) { | ||
225 | + _pageGlobalKeys = List.generate(pages.length, (_) => GlobalKey()); | ||
226 | + } | ||
227 | + | ||
200 | if (widget.pagesBuilder != null) { | 228 | if (widget.pagesBuilder != null) { |
201 | return widget.pagesBuilder!(context, pages); | 229 | return widget.pagesBuilder!(context, pages); |
202 | } | 230 | } |
203 | - return ListView.builder( | ||
204 | - controller: scrollController, | ||
205 | - shrinkWrap: widget.shrinkWrap, | ||
206 | - physics: widget.scrollPhysics, | ||
207 | - padding: widget.padding, | ||
208 | - itemCount: pages.length, | ||
209 | - itemBuilder: (BuildContext context, int index) => GestureDetector( | ||
210 | - onDoubleTap: () { | ||
211 | - setState(() { | ||
212 | - updatePosition = scrollController.position.pixels; | ||
213 | - preview = index; | ||
214 | - transformationController.value.setIdentity(); | ||
215 | - }); | ||
216 | - }, | ||
217 | - child: PdfPreviewPage( | ||
218 | - pageData: pages[index], | ||
219 | - pdfPreviewPageDecoration: widget.pdfPreviewPageDecoration, | ||
220 | - pageMargin: widget.previewPageMargin, | ||
221 | - ), | ||
222 | - ), | ||
223 | - ); | 231 | + |
232 | + Widget pageWidget(int index, {Key? key}) => GestureDetector( | ||
233 | + onDoubleTap: () { | ||
234 | + setState(() { | ||
235 | + updatePosition = scrollController.position.pixels; | ||
236 | + preview = index; | ||
237 | + transformationController.value.setIdentity(); | ||
238 | + }); | ||
239 | + }, | ||
240 | + child: PdfPreviewPage( | ||
241 | + key: key, | ||
242 | + pageData: pages[index], | ||
243 | + pdfPreviewPageDecoration: widget.pdfPreviewPageDecoration, | ||
244 | + pageMargin: widget.previewPageMargin, | ||
245 | + ), | ||
246 | + ); | ||
247 | + | ||
248 | + return widget.enableScrollToPage | ||
249 | + ? Scrollbar( | ||
250 | + controller: scrollController, | ||
251 | + child: SingleChildScrollView( | ||
252 | + controller: scrollController, | ||
253 | + physics: widget.scrollPhysics, | ||
254 | + padding: widget.padding, | ||
255 | + child: Column( | ||
256 | + children: List.generate( | ||
257 | + pages.length, | ||
258 | + (index) => pageWidget(index, key: getPageKey(index)), | ||
259 | + ), | ||
260 | + ), | ||
261 | + ), | ||
262 | + ) | ||
263 | + : ListView.builder( | ||
264 | + controller: scrollController, | ||
265 | + shrinkWrap: widget.shrinkWrap, | ||
266 | + physics: widget.scrollPhysics, | ||
267 | + padding: widget.padding, | ||
268 | + itemCount: pages.length, | ||
269 | + itemBuilder: (BuildContext context, int index) => pageWidget(index), | ||
270 | + ); | ||
224 | } | 271 | } |
225 | 272 | ||
226 | Widget _zoomPreview() { | 273 | Widget _zoomPreview() { |
@@ -21,6 +21,7 @@ import 'package:pdf/widgets.dart' as pw; | @@ -21,6 +21,7 @@ import 'package:pdf/widgets.dart' as pw; | ||
21 | import '../callback.dart'; | 21 | import '../callback.dart'; |
22 | import '../printing.dart'; | 22 | import '../printing.dart'; |
23 | import '../printing_info.dart'; | 23 | import '../printing_info.dart'; |
24 | +import 'action_bar_theme.dart'; | ||
24 | import 'actions.dart'; | 25 | import 'actions.dart'; |
25 | import 'controller.dart'; | 26 | import 'controller.dart'; |
26 | import 'custom.dart'; | 27 | import 'custom.dart'; |
@@ -62,6 +63,8 @@ class PdfPreview extends StatefulWidget { | @@ -62,6 +63,8 @@ class PdfPreview extends StatefulWidget { | ||
62 | this.loadingWidget, | 63 | this.loadingWidget, |
63 | this.onPageFormatChanged, | 64 | this.onPageFormatChanged, |
64 | this.dpi, | 65 | this.dpi, |
66 | + this.actionBarTheme = const PdfActionBarTheme(), | ||
67 | + this.enableScrollToPage = false, | ||
65 | }) : _pagesBuilder = null, | 68 | }) : _pagesBuilder = null, |
66 | super(key: key); | 69 | super(key: key); |
67 | 70 | ||
@@ -119,7 +122,9 @@ class PdfPreview extends StatefulWidget { | @@ -119,7 +122,9 @@ class PdfPreview extends StatefulWidget { | ||
119 | this.loadingWidget, | 122 | this.loadingWidget, |
120 | this.onPageFormatChanged, | 123 | this.onPageFormatChanged, |
121 | this.dpi, | 124 | this.dpi, |
125 | + this.actionBarTheme = const PdfActionBarTheme(), | ||
122 | required CustomPdfPagesBuilder pagesBuilder, | 126 | required CustomPdfPagesBuilder pagesBuilder, |
127 | + this.enableScrollToPage = false, | ||
123 | }) : _pagesBuilder = pagesBuilder, | 128 | }) : _pagesBuilder = pagesBuilder, |
124 | super(key: key); | 129 | super(key: key); |
125 | 130 | ||
@@ -223,10 +228,16 @@ class PdfPreview extends StatefulWidget { | @@ -223,10 +228,16 @@ class PdfPreview extends StatefulWidget { | ||
223 | /// If not provided, this value is calculated. | 228 | /// If not provided, this value is calculated. |
224 | final double? dpi; | 229 | final double? dpi; |
225 | 230 | ||
231 | + /// The style of actions bar. | ||
232 | + final PdfActionBarTheme actionBarTheme; | ||
233 | + | ||
226 | /// clients can pass this builder to render | 234 | /// clients can pass this builder to render |
227 | /// their own pages. | 235 | /// their own pages. |
228 | final CustomPdfPagesBuilder? _pagesBuilder; | 236 | final CustomPdfPagesBuilder? _pagesBuilder; |
229 | 237 | ||
238 | + /// Whether scroll to page functionality enabled. | ||
239 | + final bool enableScrollToPage; | ||
240 | + | ||
230 | @override | 241 | @override |
231 | PdfPreviewState createState() => PdfPreviewState(); | 242 | PdfPreviewState createState() => PdfPreviewState(); |
232 | } | 243 | } |
@@ -296,7 +307,6 @@ class PdfPreviewState extends State<PdfPreview> { | @@ -296,7 +307,6 @@ class PdfPreviewState extends State<PdfPreview> { | ||
296 | initialPageFormat: previewData.pageFormat, | 307 | initialPageFormat: previewData.pageFormat, |
297 | onComputeActualPageFormat: computeActualPageFormat, | 308 | onComputeActualPageFormat: computeActualPageFormat, |
298 | ); | 309 | ); |
299 | - setState(() {}); | ||
300 | } | 310 | } |
301 | super.didUpdateWidget(oldWidget); | 311 | super.didUpdateWidget(oldWidget); |
302 | } | 312 | } |
@@ -400,22 +410,30 @@ class PdfPreviewState extends State<PdfPreview> { | @@ -400,22 +410,30 @@ class PdfPreviewState extends State<PdfPreview> { | ||
400 | shouldRepaint: widget.shouldRepaint, | 410 | shouldRepaint: widget.shouldRepaint, |
401 | pagesBuilder: widget._pagesBuilder, | 411 | pagesBuilder: widget._pagesBuilder, |
402 | dpi: widget.dpi, | 412 | dpi: widget.dpi, |
413 | + enableScrollToPage: widget.enableScrollToPage, | ||
403 | ); | 414 | ); |
404 | }), | 415 | }), |
405 | ), | 416 | ), |
406 | if (actions.isNotEmpty) | 417 | if (actions.isNotEmpty) |
407 | IconTheme.merge( | 418 | IconTheme.merge( |
408 | data: IconThemeData( | 419 | data: IconThemeData( |
409 | - color: iconColor, | 420 | + color: widget.actionBarTheme.iconColor ?? iconColor, |
410 | ), | 421 | ), |
411 | child: Material( | 422 | child: Material( |
412 | - elevation: 4, | ||
413 | - color: theme.primaryColor, | 423 | + elevation: widget.actionBarTheme.elevation, |
424 | + color: | ||
425 | + widget.actionBarTheme.backgroundColor ?? theme.primaryColor, | ||
426 | + textStyle: widget.actionBarTheme.textStyle, | ||
414 | child: SizedBox( | 427 | child: SizedBox( |
415 | width: double.infinity, | 428 | width: double.infinity, |
429 | + height: widget.actionBarTheme.height, | ||
416 | child: SafeArea( | 430 | child: SafeArea( |
417 | child: Wrap( | 431 | child: Wrap( |
418 | - alignment: WrapAlignment.spaceAround, | 432 | + spacing: widget.actionBarTheme.actionSpacing, |
433 | + alignment: widget.actionBarTheme.alignment, | ||
434 | + runAlignment: widget.actionBarTheme.runAlignment, | ||
435 | + crossAxisAlignment: | ||
436 | + widget.actionBarTheme.crossAxisAlignment, | ||
419 | children: actions, | 437 | children: actions, |
420 | ), | 438 | ), |
421 | ), | 439 | ), |
-
Please register or login to post a comment