├── .gitignore ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── example ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── lib │ └── main.dart ├── pubspec.yaml ├── test │ └── widget_test.dart └── web │ ├── favicon.png │ ├── icons │ ├── Icon-192.png │ └── Icon-512.png │ ├── index.html │ └── manifest.json ├── lib ├── src │ ├── source_code_view.dart │ ├── syntax_highlighter.dart │ └── widget_with_codeview.dart └── widget_with_codeview.dart ├── pubspec.lock └── pubspec.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | .flutter-plugins-dependencies 2 | # Miscellaneous 3 | *.class 4 | *.log 5 | *.pyc 6 | *.swp 7 | .DS_Store 8 | .atom/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | .vscode/ 13 | 14 | # IntelliJ related 15 | *.iml 16 | *.ipr 17 | *.iws 18 | .idea/ 19 | 20 | # The .vscode folder contains launch configuration and tasks you configure in 21 | # VS Code which you may wish to be included in version control, so this line 22 | # is commented out by default. 23 | #.vscode/ 24 | 25 | # Flutter/Dart/Pub related 26 | **/doc/api/ 27 | .dart_tool/ 28 | .flutter-plugins 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | build/ 33 | pubspec.lock 34 | 35 | # Android related 36 | **/android/**/gradle-wrapper.jar 37 | **/android/.gradle 38 | **/android/captures/ 39 | **/android/gradlew 40 | **/android/gradlew.bat 41 | **/android/local.properties 42 | **/android/**/GeneratedPluginRegistrant.java 43 | 44 | # iOS/XCode related 45 | **/ios/**/*.mode1v3 46 | **/ios/**/*.mode2v3 47 | **/ios/**/*.moved-aside 48 | **/ios/**/*.pbxuser 49 | **/ios/**/*.perspectivev3 50 | **/ios/**/*sync/ 51 | **/ios/**/.sconsign.dblite 52 | **/ios/**/.tags* 53 | **/ios/**/.vagrant/ 54 | **/ios/**/DerivedData/ 55 | **/ios/**/Icon? 56 | **/ios/**/Pods/ 57 | **/ios/**/.symlinks/ 58 | **/ios/**/profile 59 | **/ios/**/xcuserdata 60 | **/ios/.generated/ 61 | **/ios/Flutter/App.framework 62 | **/ios/Flutter/Flutter.framework 63 | **/ios/Flutter/Generated.xcconfig 64 | **/ios/Flutter/app.flx 65 | **/ios/Flutter/app.zip 66 | **/ios/Flutter/flutter_assets/ 67 | **/ios/ServiceDefinitions.json 68 | **/ios/Runner/GeneratedPluginRegistrant.* 69 | 70 | # Exceptions to above rules. 71 | !**/ios/**/default.mode1v3 72 | !**/ios/**/default.mode2v3 73 | !**/ios/**/default.pbxuser 74 | !**/ios/**/default.perspectivev3 75 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 76 | example/.gitignore 77 | example/pubspec.lock 78 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 20e59316b8b8474554b38493b8ca888794b0234a 8 | channel: beta 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelogs 2 | 3 | ## [3.1.0] - 2023-07-25 4 | - upgrade packages 5 | 6 | ## [3.0.1] - 2022-10-23 7 | - [breaking] migrate to dart 2.17. 8 | - [breaking] unify to one single `WidgetWithCodeView` API 9 | - when `child` argument is null, will only show `SourceCodeView`. 10 | - See https://github.com/X-Wei/widget_with_codeview/issues/10. 11 | - use [flutter_highlight](https://pub.dev/packages/flutter_highlight) for codeView 12 | - add param `tabChangeListener` to listen tab switch event 13 | - add param `headerWidget`/`footerWidget` 14 | - add param `lightTheme`/`darkTheme` to specify code syntax highlighter theme. 15 | - fix speedial style 16 | 17 | ## [2.0.1] - 2022-01-23 18 | - Many improvements thanks to Agondev's [PR](https://github.com/X-Wei/widget_with_codeview/pull/11) 19 | - Fix speedDial menu lable color [issue](https://github.com/X-Wei/flutter_catalog/issues/120). 20 | 21 | ## [2.0.0-nullsafe] - 2021-03-26 22 | 23 | (Prerelease) Migrate to null safty. 24 | Many thanks to ryan-sf@! 25 | 26 | ## [1.0.5] - 2020-11-14 27 | 28 | Use google_fonts package for source code view. 29 | 30 | ## [1.0.4] - 2020-11-08 31 | 32 | Fix default tab controller. 33 | 34 | ## [1.0.3] - 2019-11-02 35 | 36 | Fix zoom button behavior. 37 | 38 | ## [1.0.2] - 2019-10-19 39 | 40 | Use SelectableText.rich() to make code selectable. 41 | 42 | ## [1.0.1] - 2019-07-27 43 | 44 | Fix multiple heroes tag issue. 45 | 46 | ## [1.0.0] - 2019-07-22 47 | 48 | Initial version: a small tweak from the MyRoute class in 49 | 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 X-Wei 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # widget_with_codeview 2 | 3 | [![pub package](https://img.shields.io/pub/v/widget_with_codeview.svg)](https://pub.dartlang.org/packages/widget_with_codeview) 4 | ![GitHub](https://img.shields.io/github/license/x-wei/widget_with_codeview.svg) 5 | 6 | A widget with side-by-side source code view. Extracted from the 7 | [flutter-catalog](https://github.com/X-Wei/flutter_catalog/) open-source app. 8 | 9 | 10 | 11 | 12 | ## Usage 13 | 14 | First make sure to add the source file to the app's assets by editing `pubspec.yaml`: 15 | 16 | ```yaml 17 | dependencies: 18 | flutter: 19 | sdk: flutter 20 | widget_with_codeview: 21 | flutter: 22 | assets: 23 | # Include a single source code file: 24 | - lib/my_awesome_source_code.dart 25 | # Include all files under a subfoler by adding trailing "/": 26 | - lib/my_awesome_source_code_subdir/ 27 | - ... 28 | ``` 29 | 30 | Then wrap the widget from that source file by a `WidgetWithCodeView`: 31 | 32 | ```dart 33 | WidgetWithCodeView( 34 | child: MyAwesomeWidget(), // ⚡️ If null, will only show the source code view. 35 | filePath: 'lib/my_awesome_source_code.dart', 36 | /// [codeLinkPrefix] is optional. When it's specified, two more buttons 37 | /// (open-code-in-browser, copy-code-link) will be added in the code view. 38 | codeLinkPrefix: 'https://github.com///blob/master/', 39 | ), 40 | ``` 41 | 42 | You can also choose to only show the code by not setting the `child` argument. 43 | 44 | See `example/lib/main.dart` for a concrete example. 45 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # Ignoring native folders of the example as they can be re-generated easily 13 | **/android/ 14 | **/ios/ 15 | **/macos/ 16 | **/windows/ 17 | **/web/ 18 | 19 | # IntelliJ related 20 | *.iml 21 | *.ipr 22 | *.iws 23 | .idea/ 24 | 25 | # The .vscode folder contains launch configuration and tasks you configure in 26 | # VS Code which you may wish to be included in version control, so this line 27 | # is commented out by default. 28 | #.vscode/ 29 | 30 | # Flutter/Dart/Pub related 31 | **/doc/api/ 32 | **/ios/Flutter/.last_build_id 33 | .dart_tool/ 34 | .flutter-plugins 35 | .flutter-plugins-dependencies 36 | .packages 37 | .pub-cache/ 38 | .pub/ 39 | /build/ 40 | 41 | # Web related 42 | lib/generated_plugin_registrant.dart 43 | 44 | # Symbolication related 45 | app.*.symbols 46 | 47 | # Obfuscation related 48 | app.*.map.json 49 | 50 | # Android Studio will place build artifacts here 51 | /android/app/debug 52 | /android/app/profile 53 | /android/app/release 54 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled. 5 | 6 | version: 7 | revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 8 | channel: stable 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 17 | base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 18 | - platform: android 19 | create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 20 | base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 21 | - platform: ios 22 | create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 23 | base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 24 | - platform: macos 25 | create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 26 | base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 27 | - platform: web 28 | create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 29 | base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 30 | - platform: windows 31 | create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 32 | base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 33 | 34 | # User provided section 35 | 36 | # List of Local paths (relative to this file) that should be 37 | # ignored by the migrate tool. 38 | # 39 | # Files that are not part of the templates will be ignored by default. 40 | unmanaged_files: 41 | - 'lib/main.dart' 42 | - 'ios/Runner.xcodeproj/project.pbxproj' 43 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # example 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | 11 | linter: 12 | # The lint rules applied to this project can be customized in the 13 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 14 | # included above or to enable additional rules. A list of all available lints 15 | # and their documentation is published at 16 | # https://dart-lang.github.io/linter/lints/index.html. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 26 | 27 | # Additional information about this file can be found at 28 | # https://dart.dev/guides/language/analysis-options 29 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:widget_with_codeview/widget_with_codeview.dart'; 5 | 6 | void main() { 7 | runApp(MyApp()); 8 | } 9 | 10 | /// ! Ensure the files in [filePath] are included in pubspec.yaml 11 | /// In this example the codeViewer will display this file's code 12 | /// by pointing to the main.dart file 13 | class MyApp extends StatelessWidget { 14 | @override 15 | Widget build(BuildContext context) { 16 | return MaterialApp( 17 | theme: ThemeData.dark(), 18 | home: Scaffold( 19 | body: WidgetWithCodeView( 20 | child: const SomeWidget(), //! If empty, will only show the code view. 21 | filePath: 'lib/main.dart', 22 | // codeContent: '', 23 | codeLinkPrefix: 'https://google.com?q=', 24 | iconBackgroundColor: Colors.white, 25 | iconForegroundColor: Colors.pink, 26 | labelBackgroundColor: Theme.of(context).canvasColor, 27 | labelTextStyle: 28 | TextStyle(color: Theme.of(context).textTheme.bodyLarge!.color), 29 | showLabelText: true, 30 | ), 31 | ), 32 | ); 33 | } 34 | } 35 | 36 | class SomeWidget extends StatelessWidget { 37 | const SomeWidget({Key? key}) : super(key: key); 38 | 39 | @override 40 | Widget build(BuildContext context) => Stack( 41 | children: [ 42 | Center( 43 | child: Transform.rotate( 44 | angle: Random().nextDouble(), 45 | child: Text( 46 | 'Example', 47 | textScaleFactor: 2, 48 | ), 49 | ), 50 | ), 51 | Wrap( 52 | children: List.generate( 53 | 100, 54 | (_) => SizedBox( 55 | width: MediaQuery.of(context).size.width * .25, 56 | height: MediaQuery.of(context).size.width * .25, 57 | child: Placeholder( 58 | color: Colors.accents[Random().nextInt( 59 | Colors.accents.length, 60 | )], 61 | ), 62 | ), 63 | ), 64 | ), 65 | ], 66 | ); 67 | } 68 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: A new Flutter project. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | version: 1.0.0+1 9 | 10 | environment: 11 | sdk: '>=2.17.0 <3.0.0' 12 | 13 | dependencies: 14 | flutter: 15 | sdk: flutter 16 | 17 | cupertino_icons: ^1.0.5 18 | widget_with_codeview: 19 | path: ../ 20 | 21 | dev_dependencies: 22 | flutter_test: 23 | sdk: flutter 24 | 25 | # For information on the generic Dart part of this file, see the 26 | # following page: https://dart.dev/tools/pub/pubspec 27 | 28 | # The following section is specific to Flutter. 29 | flutter: 30 | uses-material-design: true 31 | 32 | assets: 33 | - lib/ 34 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | // import 'package:example/main.dart'; 9 | // import 'package:flutter_test/flutter_test.dart'; 10 | 11 | void main() { 12 | // testWidgets('smoke test', (WidgetTester tester) async { 13 | // // Build our app and trigger a frame. 14 | // // await tester.pumpWidget(MyApp()); 15 | 16 | // // expect(find.text('1'), findsNothing); 17 | // }); 18 | } 19 | -------------------------------------------------------------------------------- /example/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/X-Wei/widget_with_codeview/d98d7d9024e8c5e2ca2b905f3b90f3b73a336470/example/web/favicon.png -------------------------------------------------------------------------------- /example/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/X-Wei/widget_with_codeview/d98d7d9024e8c5e2ca2b905f3b90f3b73a336470/example/web/icons/Icon-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/X-Wei/widget_with_codeview/d98d7d9024e8c5e2ca2b905f3b90f3b73a336470/example/web/icons/Icon-512.png -------------------------------------------------------------------------------- /example/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | example 30 | 31 | 32 | 33 | 36 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "short_name": "example", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /lib/src/source_code_view.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:flutter_highlight/flutter_highlight.dart'; 6 | import 'package:flutter_highlight/themes/atom-one-dark.dart'; 7 | import 'package:flutter_highlight/themes/atom-one-light.dart'; 8 | import 'package:flutter_speed_dial/flutter_speed_dial.dart'; 9 | import 'package:google_fonts/google_fonts.dart'; 10 | import 'package:selectable/selectable.dart'; 11 | import 'package:url_launcher/url_launcher.dart' as url_launcher; 12 | 13 | class SourceCodeView extends StatefulWidget { 14 | // Path of source file (relative to project root). The file's content will be 15 | // shown in the "Code" tab. 16 | final String? filePath; 17 | final String? codeContent; 18 | final String? codeLinkPrefix; 19 | // Fine tune the menu appearance. 20 | final bool showLabelText; 21 | final Color? iconBackgroundColor; 22 | final Color? iconForegroundColor; 23 | final Color? labelBackgroundColor; 24 | final TextStyle? labelTextStyle; 25 | // Widget to put before/after the code content. 26 | final Widget? headerWidget; 27 | final Widget? footerWidget; 28 | // Code highlighter theme for light/dark theme, defaults to "atomOne" themes. 29 | final Map? lightTheme; 30 | final Map? darkTheme; 31 | 32 | const SourceCodeView({ 33 | Key? key, 34 | required this.filePath, 35 | this.codeContent, 36 | this.codeLinkPrefix, 37 | this.showLabelText = false, 38 | this.iconBackgroundColor, 39 | this.iconForegroundColor, 40 | this.labelBackgroundColor, 41 | this.labelTextStyle, 42 | this.headerWidget, 43 | this.footerWidget, 44 | this.lightTheme, 45 | this.darkTheme, 46 | }) : super(key: key); 47 | 48 | String? get codeLink => this.codeLinkPrefix == null 49 | ? null 50 | : '${this.codeLinkPrefix}/${this.filePath}'; 51 | 52 | @override 53 | SourceCodeViewState createState() { 54 | return SourceCodeViewState(); 55 | } 56 | } 57 | 58 | class SourceCodeViewState extends State { 59 | double _textScaleFactor = 1.0; 60 | ScrollController scrollController = ScrollController(); 61 | 62 | Widget _getCodeView(String codeContent, BuildContext context) { 63 | codeContent = codeContent.replaceAll('\r\n', '\n'); 64 | return Container( 65 | constraints: BoxConstraints.expand(), 66 | child: Scrollbar( 67 | controller: scrollController, 68 | child: SingleChildScrollView( 69 | controller: scrollController, 70 | child: Column( 71 | children: [ 72 | if (widget.headerWidget != null) ...[ 73 | widget.headerWidget!, 74 | Divider(), 75 | ], 76 | SingleChildScrollView( 77 | scrollDirection: Axis.horizontal, 78 | child: Selectable( 79 | child: HighlightView( 80 | codeContent, 81 | language: 'dart', 82 | theme: Theme.of(context).brightness == Brightness.light 83 | ? widget.lightTheme ?? atomOneLightTheme 84 | : widget.darkTheme ?? atomOneDarkTheme, 85 | textStyle: GoogleFonts.notoSansMono(fontSize: 12) 86 | .apply(fontSizeFactor: this._textScaleFactor), 87 | ), 88 | ), 89 | ), 90 | if (widget.footerWidget != null) ...[ 91 | Divider(), 92 | widget.footerWidget!, 93 | ], 94 | ], 95 | ), 96 | ), 97 | ), 98 | ); 99 | } 100 | 101 | List _buildFloatingButtons({ 102 | TextStyle? labelTextStyle, 103 | Color? iconBackgroundColor, 104 | Color? iconForegroundColor, 105 | Color? labelBackgroundColor, 106 | required bool showLabelText, 107 | }) => 108 | [ 109 | if (this.widget.codeLink != null) 110 | SpeedDialChild( 111 | child: Icon(Icons.content_copy), 112 | label: showLabelText ? 'Copy code to clipboard' : null, 113 | backgroundColor: iconBackgroundColor, 114 | foregroundColor: iconForegroundColor, 115 | labelBackgroundColor: labelBackgroundColor, 116 | labelStyle: labelTextStyle, 117 | onTap: () async { 118 | if (widget.codeContent != null) { 119 | Clipboard.setData(ClipboardData(text: widget.codeContent!)); 120 | } else if (widget.filePath?.isNotEmpty ?? false) { 121 | Clipboard.setData(ClipboardData( 122 | text: await DefaultAssetBundle.of(context) 123 | .loadString(widget.filePath ?? ''))); 124 | } 125 | ScaffoldMessenger.of(context).showSnackBar(SnackBar( 126 | content: Text('Code copied to clipboard!'), 127 | )); 128 | }, 129 | ), 130 | if (this.widget.codeLink != null) 131 | SpeedDialChild( 132 | child: Icon(Icons.open_in_new), 133 | label: showLabelText ? 'View code in browser' : null, 134 | backgroundColor: iconBackgroundColor, 135 | foregroundColor: iconForegroundColor, 136 | labelBackgroundColor: labelBackgroundColor, 137 | labelStyle: labelTextStyle, 138 | onTap: () => url_launcher.launchUrl(Uri.parse(widget.codeLink!)), 139 | ), 140 | SpeedDialChild( 141 | child: Icon(Icons.zoom_out), 142 | label: showLabelText ? 'Zoom out' : null, 143 | backgroundColor: iconBackgroundColor, 144 | foregroundColor: iconForegroundColor, 145 | labelBackgroundColor: labelBackgroundColor, 146 | labelStyle: labelTextStyle, 147 | onTap: () => setState(() { 148 | this._textScaleFactor = max(0.8, this._textScaleFactor - 0.1); 149 | }), 150 | ), 151 | SpeedDialChild( 152 | child: Icon(Icons.zoom_in), 153 | label: showLabelText ? 'Zoom in' : null, 154 | backgroundColor: iconBackgroundColor, 155 | foregroundColor: iconForegroundColor, 156 | labelBackgroundColor: labelBackgroundColor, 157 | labelStyle: labelTextStyle, 158 | onTap: () => setState(() { 159 | this._textScaleFactor += 0.1; 160 | }), 161 | ), 162 | ]; 163 | 164 | @override 165 | Widget build(BuildContext context) { 166 | return FutureBuilder( 167 | future: widget.codeContent != null 168 | ? Future.value(widget.codeContent) 169 | : DefaultAssetBundle.of(context).loadString(widget.filePath ?? ''), 170 | builder: (BuildContext context, AsyncSnapshot snapshot) { 171 | if (snapshot.hasData) { 172 | return Scaffold( 173 | body: Padding( 174 | padding: EdgeInsets.all(4.0), 175 | child: _getCodeView(snapshot.data!, context), 176 | ), 177 | floatingActionButton: SpeedDial( 178 | closeManually: true, 179 | children: _buildFloatingButtons( 180 | labelTextStyle: widget.labelTextStyle, 181 | iconBackgroundColor: widget.iconBackgroundColor, 182 | iconForegroundColor: widget.iconForegroundColor, 183 | labelBackgroundColor: widget.labelBackgroundColor, 184 | showLabelText: widget.showLabelText, 185 | ), 186 | backgroundColor: Colors.blue, 187 | foregroundColor: Colors.white, 188 | activeBackgroundColor: Colors.red, 189 | activeForegroundColor: Colors.white, 190 | animatedIcon: AnimatedIcons.menu_close, 191 | ), 192 | ); 193 | } else { 194 | return Center(child: CircularProgressIndicator()); 195 | } 196 | }, 197 | ); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /lib/src/syntax_highlighter.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | import 'package:flutter/material.dart'; 6 | import 'package:string_scanner/string_scanner.dart'; 7 | 8 | class SyntaxHighlighterStyle { 9 | SyntaxHighlighterStyle( 10 | {this.baseStyle, 11 | this.numberStyle, 12 | this.commentStyle, 13 | this.keywordStyle, 14 | this.stringStyle, 15 | this.punctuationStyle, 16 | this.classStyle, 17 | this.constantStyle}); 18 | 19 | static SyntaxHighlighterStyle lightThemeStyle() => SyntaxHighlighterStyle( 20 | baseStyle: const TextStyle(color: const Color(0xFF000000)), 21 | numberStyle: const TextStyle(color: const Color(0xFF1565C0)), 22 | commentStyle: const TextStyle(color: const Color(0xFF9E9E9E)), 23 | keywordStyle: const TextStyle(color: const Color(0xFF9C27B0)), 24 | stringStyle: const TextStyle(color: const Color(0xFF43A047)), 25 | punctuationStyle: const TextStyle(color: const Color(0xFF000000)), 26 | classStyle: const TextStyle(color: const Color(0xFF512DA8)), 27 | constantStyle: const TextStyle(color: const Color(0xFF795548)), 28 | ); 29 | 30 | static SyntaxHighlighterStyle darkThemeStyle() => SyntaxHighlighterStyle( 31 | baseStyle: const TextStyle(color: const Color(0xFFFFFFFF)), 32 | numberStyle: const TextStyle(color: const Color(0xFF1565C0)), 33 | commentStyle: const TextStyle(color: const Color(0xFF9E9E9E)), 34 | keywordStyle: const TextStyle(color: const Color(0xFF80CBC4)), 35 | stringStyle: const TextStyle(color: const Color(0xFF009688)), 36 | punctuationStyle: const TextStyle(color: const Color(0xFFFFFFFF)), 37 | classStyle: const TextStyle(color: const Color(0xFF009688)), 38 | constantStyle: const TextStyle(color: const Color(0xFF795548)), 39 | ); 40 | 41 | SyntaxHighlighterStyle copyWith({ 42 | TextStyle? baseStyle, 43 | TextStyle? numberStyle, 44 | TextStyle? commentStyle, 45 | TextStyle? keywordStyle, 46 | TextStyle? stringStyle, 47 | TextStyle? punctuationStyle, 48 | TextStyle? classStyle, 49 | TextStyle? constantStyle, 50 | }) => 51 | SyntaxHighlighterStyle( 52 | baseStyle: baseStyle ?? this.baseStyle, 53 | numberStyle: numberStyle ?? this.numberStyle, 54 | commentStyle: commentStyle ?? this.commentStyle, 55 | keywordStyle: keywordStyle ?? this.keywordStyle, 56 | stringStyle: stringStyle ?? this.stringStyle, 57 | punctuationStyle: punctuationStyle ?? this.punctuationStyle, 58 | classStyle: classStyle ?? this.classStyle, 59 | constantStyle: constantStyle ?? this.constantStyle, 60 | ); 61 | 62 | final TextStyle? baseStyle; 63 | final TextStyle? numberStyle; 64 | final TextStyle? commentStyle; 65 | final TextStyle? keywordStyle; 66 | final TextStyle? stringStyle; 67 | final TextStyle? punctuationStyle; 68 | final TextStyle? classStyle; 69 | final TextStyle? constantStyle; 70 | } 71 | 72 | abstract class SyntaxHighlighter { 73 | // ignore: one_member_abstracts 74 | TextSpan format(String src); 75 | } 76 | 77 | class DartSyntaxHighlighter extends SyntaxHighlighter { 78 | DartSyntaxHighlighter([this._style]) { 79 | _spans = <_HighlightSpan>[]; 80 | _style ??= SyntaxHighlighterStyle.darkThemeStyle(); 81 | } 82 | 83 | SyntaxHighlighterStyle? _style; 84 | 85 | static const List _keywords = const [ 86 | 'abstract', 87 | 'as', 88 | 'assert', 89 | 'async', 90 | 'await', 91 | 'break', 92 | 'case', 93 | 'catch', 94 | 'class', 95 | 'const', 96 | 'continue', 97 | 'default', 98 | 'deferred', 99 | 'do', 100 | 'dynamic', 101 | 'else', 102 | 'enum', 103 | 'export', 104 | 'external', 105 | 'extends', 106 | 'factory', 107 | 'false', 108 | 'final', 109 | 'finally', 110 | 'for', 111 | 'get', 112 | 'if', 113 | 'implements', 114 | 'import', 115 | 'in', 116 | 'is', 117 | 'library', 118 | 'new', 119 | 'null', 120 | 'operator', 121 | 'part', 122 | 'rethrow', 123 | 'return', 124 | 'set', 125 | 'static', 126 | 'super', 127 | 'switch', 128 | 'sync', 129 | 'this', 130 | 'throw', 131 | 'true', 132 | 'try', 133 | 'typedef', 134 | 'var', 135 | 'void', 136 | 'while', 137 | 'with', 138 | 'yield' 139 | ]; 140 | 141 | static const List _builtInTypes = const [ 142 | 'int', 143 | 'double', 144 | 'num', 145 | 'bool' 146 | ]; 147 | 148 | late String _src; 149 | late StringScanner _scanner; 150 | 151 | late List<_HighlightSpan> _spans; 152 | 153 | @override 154 | TextSpan format(String src) { 155 | _src = src; 156 | _scanner = StringScanner(_src); 157 | 158 | if (_generateSpans()) { 159 | // Successfully parsed the code 160 | final List formattedText = []; 161 | int currentPosition = 0; 162 | 163 | for (_HighlightSpan span in _spans) { 164 | if (currentPosition != span.start) 165 | formattedText 166 | .add(TextSpan(text: _src.substring(currentPosition, span.start))); 167 | 168 | formattedText.add(TextSpan( 169 | style: span.textStyle(_style), text: span.textForSpan(_src))); 170 | 171 | currentPosition = span.end; 172 | } 173 | 174 | if (currentPosition != _src.length) 175 | formattedText 176 | .add(TextSpan(text: _src.substring(currentPosition, _src.length))); 177 | 178 | return TextSpan(style: _style!.baseStyle, children: formattedText); 179 | } else { 180 | // Parsing failed, return with only basic formatting 181 | return TextSpan(style: _style!.baseStyle, text: src); 182 | } 183 | } 184 | 185 | bool _generateSpans() { 186 | int lastLoopPosition = _scanner.position; 187 | 188 | while (!_scanner.isDone) { 189 | // Skip White space 190 | _scanner.scan(RegExp(r'\s+')); 191 | 192 | // Block comments 193 | if (_scanner.scan(RegExp(r'/\*(.|\n)*\*/'))) { 194 | _spans.add(_HighlightSpan(_HighlightType.comment, 195 | _scanner.lastMatch!.start, _scanner.lastMatch!.end)); 196 | continue; 197 | } 198 | 199 | // Line comments 200 | if (_scanner.scan('//')) { 201 | final int startComment = _scanner.lastMatch!.start; 202 | 203 | bool eof = false; 204 | int endComment; 205 | if (_scanner.scan(RegExp(r'.*\n'))) { 206 | endComment = _scanner.lastMatch!.end - 1; 207 | } else { 208 | eof = true; 209 | endComment = _src.length; 210 | } 211 | 212 | _spans.add( 213 | _HighlightSpan(_HighlightType.comment, startComment, endComment)); 214 | 215 | if (eof) break; 216 | 217 | continue; 218 | } 219 | 220 | // Raw r"String" 221 | if (_scanner.scan(RegExp(r'r".*"'))) { 222 | _spans.add(_HighlightSpan(_HighlightType.string, 223 | _scanner.lastMatch!.start, _scanner.lastMatch!.end)); 224 | continue; 225 | } 226 | 227 | // Raw r'String' 228 | if (_scanner.scan(RegExp(r"r'.*'"))) { 229 | _spans.add(_HighlightSpan(_HighlightType.string, 230 | _scanner.lastMatch!.start, _scanner.lastMatch!.end)); 231 | continue; 232 | } 233 | 234 | // Multiline """String""" 235 | if (_scanner.scan(RegExp(r'"""(?:[^"\\]|\\(.|\n))*"""'))) { 236 | _spans.add(_HighlightSpan(_HighlightType.string, 237 | _scanner.lastMatch!.start, _scanner.lastMatch!.end)); 238 | continue; 239 | } 240 | 241 | // Multiline '''String''' 242 | if (_scanner.scan(RegExp(r"'''(?:[^'\\]|\\(.|\n))*'''"))) { 243 | _spans.add(_HighlightSpan(_HighlightType.string, 244 | _scanner.lastMatch!.start, _scanner.lastMatch!.end)); 245 | continue; 246 | } 247 | 248 | // "String" 249 | if (_scanner.scan(RegExp(r'"(?:[^"\\]|\\.)*"'))) { 250 | _spans.add(_HighlightSpan(_HighlightType.string, 251 | _scanner.lastMatch!.start, _scanner.lastMatch!.end)); 252 | continue; 253 | } 254 | 255 | // 'String' 256 | if (_scanner.scan(RegExp(r"'(?:[^'\\]|\\.)*'"))) { 257 | _spans.add(_HighlightSpan(_HighlightType.string, 258 | _scanner.lastMatch!.start, _scanner.lastMatch!.end)); 259 | continue; 260 | } 261 | 262 | // Double 263 | if (_scanner.scan(RegExp(r'\d+\.\d+'))) { 264 | _spans.add(_HighlightSpan(_HighlightType.number, 265 | _scanner.lastMatch!.start, _scanner.lastMatch!.end)); 266 | continue; 267 | } 268 | 269 | // Integer 270 | if (_scanner.scan(RegExp(r'\d+'))) { 271 | _spans.add(_HighlightSpan(_HighlightType.number, 272 | _scanner.lastMatch!.start, _scanner.lastMatch!.end)); 273 | continue; 274 | } 275 | 276 | // Punctuation 277 | if (_scanner.scan(RegExp(r'[\[\]{}().!=<>&\|\?\+\-\*/%\^~;:,]'))) { 278 | _spans.add(_HighlightSpan(_HighlightType.punctuation, 279 | _scanner.lastMatch!.start, _scanner.lastMatch!.end)); 280 | continue; 281 | } 282 | 283 | // Meta data 284 | if (_scanner.scan(RegExp(r'@\w+'))) { 285 | _spans.add(_HighlightSpan(_HighlightType.keyword, 286 | _scanner.lastMatch!.start, _scanner.lastMatch!.end)); 287 | continue; 288 | } 289 | 290 | // Words 291 | if (_scanner.scan(RegExp(r'\w+'))) { 292 | _HighlightType? type; 293 | 294 | String word = _scanner.lastMatch![0]!; 295 | if (word.startsWith('_')) word = word.substring(1); 296 | 297 | if (_keywords.contains(word)) 298 | type = _HighlightType.keyword; 299 | else if (_builtInTypes.contains(word)) 300 | type = _HighlightType.keyword; 301 | else if (_firstLetterIsUpperCase(word)) 302 | type = _HighlightType.klass; 303 | else if (word.length >= 2 && 304 | word.startsWith('k') && 305 | _firstLetterIsUpperCase(word.substring(1))) 306 | type = _HighlightType.constant; 307 | 308 | if (type != null) { 309 | _spans.add(_HighlightSpan( 310 | type, _scanner.lastMatch!.start, _scanner.lastMatch!.end)); 311 | } 312 | } 313 | 314 | // Check if this loop did anything 315 | if (lastLoopPosition == _scanner.position) { 316 | // Failed to parse this file, abort gracefully 317 | return false; 318 | } 319 | lastLoopPosition = _scanner.position; 320 | } 321 | 322 | _simplify(); 323 | return true; 324 | } 325 | 326 | void _simplify() { 327 | for (int i = _spans.length - 2; i >= 0; i -= 1) { 328 | if (_spans[i].type == _spans[i + 1].type && 329 | _spans[i].end == _spans[i + 1].start) { 330 | _spans[i] = 331 | _HighlightSpan(_spans[i].type, _spans[i].start, _spans[i + 1].end); 332 | _spans.removeAt(i + 1); 333 | } 334 | } 335 | } 336 | 337 | bool _firstLetterIsUpperCase(String str) { 338 | if (str.isNotEmpty) { 339 | final String first = str.substring(0, 1); 340 | return first == first.toUpperCase(); 341 | } 342 | return false; 343 | } 344 | } 345 | 346 | enum _HighlightType { 347 | number, 348 | comment, 349 | keyword, 350 | string, 351 | punctuation, 352 | klass, 353 | constant 354 | } 355 | 356 | class _HighlightSpan { 357 | _HighlightSpan(this.type, this.start, this.end); 358 | final _HighlightType type; 359 | final int start; 360 | final int end; 361 | 362 | String textForSpan(String src) { 363 | return src.substring(start, end); 364 | } 365 | 366 | TextStyle? textStyle(SyntaxHighlighterStyle? style) { 367 | if (type == _HighlightType.number) 368 | return style!.numberStyle; 369 | else if (type == _HighlightType.comment) 370 | return style!.commentStyle; 371 | else if (type == _HighlightType.keyword) 372 | return style!.keywordStyle; 373 | else if (type == _HighlightType.string) 374 | return style!.stringStyle; 375 | else if (type == _HighlightType.punctuation) 376 | return style!.punctuationStyle; 377 | else if (type == _HighlightType.klass) 378 | return style!.classStyle; 379 | else if (type == _HighlightType.constant) 380 | return style!.constantStyle; 381 | else 382 | return style!.baseStyle; 383 | } 384 | } 385 | -------------------------------------------------------------------------------- /lib/src/widget_with_codeview.dart: -------------------------------------------------------------------------------- 1 | library widget_with_codeview; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | import 'source_code_view.dart'; 6 | 7 | class WidgetWithCodeView extends SourceCodeView { 8 | // If not given, will just show the SourceCodeView (see https://github.com/X-Wei/widget_with_codeview/issues/10). 9 | final Widget? child; 10 | // Can be used to add a hook when switching tabs. 11 | final void Function(TabController)? tabChangeListener; 12 | 13 | const WidgetWithCodeView({ 14 | super.key, 15 | required super.filePath, 16 | super.codeContent, 17 | this.child, 18 | this.tabChangeListener, 19 | super.codeLinkPrefix, 20 | super.showLabelText, 21 | super.iconBackgroundColor, 22 | super.iconForegroundColor, 23 | super.labelBackgroundColor, 24 | super.labelTextStyle, 25 | super.headerWidget, 26 | super.footerWidget, 27 | super.lightTheme, 28 | super.darkTheme, 29 | }); 30 | 31 | static const _TABS = [ 32 | Tab( 33 | child: ListTile( 34 | leading: Icon(Icons.phone_android, color: Colors.white), 35 | title: Text('Preview', style: TextStyle(color: Colors.white)), 36 | ), 37 | ), 38 | Tab( 39 | child: ListTile( 40 | leading: Icon(Icons.code, color: Colors.white), 41 | title: Text('Code', style: TextStyle(color: Colors.white)), 42 | ), 43 | ), 44 | ]; 45 | 46 | @override 47 | _WidgetWithCodeViewState createState() => _WidgetWithCodeViewState( 48 | tabChangeListener: tabChangeListener, child: child); 49 | } 50 | 51 | //? Need to override SourceCodeViewState rather than State. 52 | //! otherwise won't compile, because WidgetWithCodeView extends SourceCodeView. 53 | //! I use this inheritance to use "parameter forwarding" feature in dart 2.17. 54 | class _WidgetWithCodeViewState extends SourceCodeViewState 55 | with SingleTickerProviderStateMixin { 56 | Widget? child; 57 | late TabController _tabController; 58 | void Function(TabController)? tabChangeListener; 59 | 60 | _WidgetWithCodeViewState({this.child, this.tabChangeListener}); 61 | 62 | @override 63 | void initState() { 64 | super.initState(); 65 | _tabController = TabController(length: 2, vsync: this); 66 | if (tabChangeListener != null) { 67 | _tabController.addListener( 68 | () => tabChangeListener!(_tabController), 69 | ); 70 | } 71 | } 72 | 73 | @override 74 | void dispose() { 75 | _tabController.dispose(); 76 | super.dispose(); 77 | } 78 | 79 | String get routeName => '/${this.runtimeType.toString()}'; 80 | 81 | @override 82 | Widget build(BuildContext context) { 83 | final sourceCodeView = super.build(context); 84 | return Scaffold( 85 | appBar: (child == null) 86 | ? null 87 | : _ColoredTabBar( 88 | color: Theme.of(context).primaryColor, 89 | tabBar: TabBar( 90 | controller: _tabController, 91 | tabs: WidgetWithCodeView._TABS, 92 | ), 93 | ), 94 | body: (child == null) 95 | ? sourceCodeView 96 | : TabBarView( 97 | controller: _tabController, 98 | children: [ 99 | _AlwaysAliveWidget(child: this.child!), 100 | _AlwaysAliveWidget(child: sourceCodeView), 101 | ], 102 | ), 103 | ); 104 | } 105 | } 106 | 107 | // This widget is always kept alive, so that when tab is switched back, its 108 | // child's state is still preserved. 109 | class _AlwaysAliveWidget extends StatefulWidget { 110 | final Widget child; 111 | 112 | const _AlwaysAliveWidget({Key? key, required this.child}) : super(key: key); 113 | 114 | @override 115 | _AlwaysAliveWidgetState createState() => _AlwaysAliveWidgetState(); 116 | } 117 | 118 | class _AlwaysAliveWidgetState extends State<_AlwaysAliveWidget> 119 | with AutomaticKeepAliveClientMixin<_AlwaysAliveWidget> { 120 | @override 121 | Widget build(BuildContext context) { 122 | super.build(context); // This build method is annotated "@mustCallSuper". 123 | return this.widget.child; 124 | } 125 | 126 | @override 127 | bool get wantKeepAlive => true; 128 | } 129 | 130 | class _ColoredTabBar extends Container implements PreferredSizeWidget { 131 | final Color color; 132 | final TabBar tabBar; 133 | 134 | _ColoredTabBar({Key? key, required this.color, required this.tabBar}) 135 | : super(key: key); 136 | 137 | @override 138 | Size get preferredSize => tabBar.preferredSize; 139 | 140 | @override 141 | Widget build(BuildContext context) => Material( 142 | elevation: 4.0, 143 | color: color, 144 | child: tabBar, 145 | ); 146 | } 147 | -------------------------------------------------------------------------------- /lib/widget_with_codeview.dart: -------------------------------------------------------------------------------- 1 | export 'src/source_code_view.dart'; 2 | export 'src/widget_with_codeview.dart'; 3 | export 'src/syntax_highlighter.dart' show SyntaxHighlighterStyle; 4 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "2.11.0" 12 | boolean_selector: 13 | dependency: transitive 14 | description: 15 | name: boolean_selector 16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "2.1.1" 20 | characters: 21 | dependency: transitive 22 | description: 23 | name: characters 24 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "1.3.0" 28 | clock: 29 | dependency: transitive 30 | description: 31 | name: clock 32 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "1.1.1" 36 | collection: 37 | dependency: transitive 38 | description: 39 | name: collection 40 | sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.17.1" 44 | crypto: 45 | dependency: transitive 46 | description: 47 | name: crypto 48 | sha256: "8be10341257b613566fdc9fd073c46f7c032ed329b1c732bda17aca29f2366c8" 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "3.0.0" 52 | equatable: 53 | dependency: transitive 54 | description: 55 | name: equatable 56 | sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "2.0.5" 60 | fake_async: 61 | dependency: transitive 62 | description: 63 | name: fake_async 64 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 65 | url: "https://pub.dev" 66 | source: hosted 67 | version: "1.3.1" 68 | ffi: 69 | dependency: transitive 70 | description: 71 | name: ffi 72 | sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99 73 | url: "https://pub.dev" 74 | source: hosted 75 | version: "2.0.2" 76 | file: 77 | dependency: transitive 78 | description: 79 | name: file 80 | sha256: "9fd2163d866769f60f4df8ac1dc59f52498d810c356fe78022e383dd3c57c0e1" 81 | url: "https://pub.dev" 82 | source: hosted 83 | version: "6.1.0" 84 | float_column: 85 | dependency: transitive 86 | description: 87 | name: float_column 88 | sha256: "86a4e93877403627613001b95b00e2230d3095b29600fbb312fe8a745b61a8e0" 89 | url: "https://pub.dev" 90 | source: hosted 91 | version: "2.0.1" 92 | flutter: 93 | dependency: "direct main" 94 | description: flutter 95 | source: sdk 96 | version: "0.0.0" 97 | flutter_highlight: 98 | dependency: "direct main" 99 | description: 100 | name: flutter_highlight 101 | sha256: "7b96333867aa07e122e245c033b8ad622e4e3a42a1a2372cbb098a2541d8782c" 102 | url: "https://pub.dev" 103 | source: hosted 104 | version: "0.7.0" 105 | flutter_speed_dial: 106 | dependency: "direct main" 107 | description: 108 | name: flutter_speed_dial 109 | sha256: "698a037274a66dbae8697c265440e6acb6ab6cae9ac5f95c749e7944d8f28d41" 110 | url: "https://pub.dev" 111 | source: hosted 112 | version: "7.0.0" 113 | flutter_test: 114 | dependency: "direct dev" 115 | description: flutter 116 | source: sdk 117 | version: "0.0.0" 118 | flutter_web_plugins: 119 | dependency: transitive 120 | description: flutter 121 | source: sdk 122 | version: "0.0.0" 123 | google_fonts: 124 | dependency: "direct main" 125 | description: 126 | name: google_fonts 127 | sha256: e20ff62b158b96f392bfc8afe29dee1503c94fbea2cbe8186fd59b756b8ae982 128 | url: "https://pub.dev" 129 | source: hosted 130 | version: "5.1.0" 131 | highlight: 132 | dependency: transitive 133 | description: 134 | name: highlight 135 | sha256: "5353a83ffe3e3eca7df0abfb72dcf3fa66cc56b953728e7113ad4ad88497cf21" 136 | url: "https://pub.dev" 137 | source: hosted 138 | version: "0.7.0" 139 | http: 140 | dependency: transitive 141 | description: 142 | name: http 143 | sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" 144 | url: "https://pub.dev" 145 | source: hosted 146 | version: "1.1.0" 147 | http_parser: 148 | dependency: transitive 149 | description: 150 | name: http_parser 151 | sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" 152 | url: "https://pub.dev" 153 | source: hosted 154 | version: "4.0.2" 155 | js: 156 | dependency: transitive 157 | description: 158 | name: js 159 | sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 160 | url: "https://pub.dev" 161 | source: hosted 162 | version: "0.6.7" 163 | matcher: 164 | dependency: transitive 165 | description: 166 | name: matcher 167 | sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" 168 | url: "https://pub.dev" 169 | source: hosted 170 | version: "0.12.15" 171 | material_color_utilities: 172 | dependency: transitive 173 | description: 174 | name: material_color_utilities 175 | sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 176 | url: "https://pub.dev" 177 | source: hosted 178 | version: "0.2.0" 179 | meta: 180 | dependency: transitive 181 | description: 182 | name: meta 183 | sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" 184 | url: "https://pub.dev" 185 | source: hosted 186 | version: "1.9.1" 187 | path: 188 | dependency: transitive 189 | description: 190 | name: path 191 | sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" 192 | url: "https://pub.dev" 193 | source: hosted 194 | version: "1.8.3" 195 | path_provider: 196 | dependency: transitive 197 | description: 198 | name: path_provider 199 | sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2" 200 | url: "https://pub.dev" 201 | source: hosted 202 | version: "2.0.15" 203 | path_provider_android: 204 | dependency: transitive 205 | description: 206 | name: path_provider_android 207 | sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86" 208 | url: "https://pub.dev" 209 | source: hosted 210 | version: "2.0.27" 211 | path_provider_foundation: 212 | dependency: transitive 213 | description: 214 | name: path_provider_foundation 215 | sha256: "916731ccbdce44d545414dd9961f26ba5fbaa74bcbb55237d8e65a623a8c7297" 216 | url: "https://pub.dev" 217 | source: hosted 218 | version: "2.2.4" 219 | path_provider_linux: 220 | dependency: transitive 221 | description: 222 | name: path_provider_linux 223 | sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57 224 | url: "https://pub.dev" 225 | source: hosted 226 | version: "2.1.11" 227 | path_provider_platform_interface: 228 | dependency: transitive 229 | description: 230 | name: path_provider_platform_interface 231 | sha256: c2af5a8a6369992d915f8933dfc23172071001359d17896e83db8be57db8a397 232 | url: "https://pub.dev" 233 | source: hosted 234 | version: "2.0.1" 235 | path_provider_windows: 236 | dependency: transitive 237 | description: 238 | name: path_provider_windows 239 | sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96" 240 | url: "https://pub.dev" 241 | source: hosted 242 | version: "2.1.7" 243 | platform: 244 | dependency: transitive 245 | description: 246 | name: platform 247 | sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" 248 | url: "https://pub.dev" 249 | source: hosted 250 | version: "3.1.0" 251 | plugin_platform_interface: 252 | dependency: transitive 253 | description: 254 | name: plugin_platform_interface 255 | sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a 256 | url: "https://pub.dev" 257 | source: hosted 258 | version: "2.1.3" 259 | process: 260 | dependency: transitive 261 | description: 262 | name: process 263 | sha256: dc3c073b5bc0db4e0f3dbc6b69f8e9cf2f336dafb3db996242ebdacf94c295dd 264 | url: "https://pub.dev" 265 | source: hosted 266 | version: "4.2.1" 267 | selectable: 268 | dependency: "direct main" 269 | description: 270 | name: selectable 271 | sha256: "18432ba915b3e82a367e6ec9038666e0cc3147336cba125a9aa5768dc84296eb" 272 | url: "https://pub.dev" 273 | source: hosted 274 | version: "0.3.0" 275 | sky_engine: 276 | dependency: transitive 277 | description: flutter 278 | source: sdk 279 | version: "0.0.99" 280 | source_span: 281 | dependency: transitive 282 | description: 283 | name: source_span 284 | sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 285 | url: "https://pub.dev" 286 | source: hosted 287 | version: "1.9.1" 288 | stack_trace: 289 | dependency: transitive 290 | description: 291 | name: stack_trace 292 | sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 293 | url: "https://pub.dev" 294 | source: hosted 295 | version: "1.11.0" 296 | stream_channel: 297 | dependency: transitive 298 | description: 299 | name: stream_channel 300 | sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" 301 | url: "https://pub.dev" 302 | source: hosted 303 | version: "2.1.1" 304 | string_scanner: 305 | dependency: "direct main" 306 | description: 307 | name: string_scanner 308 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 309 | url: "https://pub.dev" 310 | source: hosted 311 | version: "1.2.0" 312 | term_glyph: 313 | dependency: transitive 314 | description: 315 | name: term_glyph 316 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 317 | url: "https://pub.dev" 318 | source: hosted 319 | version: "1.2.1" 320 | test_api: 321 | dependency: transitive 322 | description: 323 | name: test_api 324 | sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb 325 | url: "https://pub.dev" 326 | source: hosted 327 | version: "0.5.1" 328 | typed_data: 329 | dependency: transitive 330 | description: 331 | name: typed_data 332 | sha256: "53bdf7e979cfbf3e28987552fd72f637e63f3c8724c9e56d9246942dc2fa36ee" 333 | url: "https://pub.dev" 334 | source: hosted 335 | version: "1.3.0" 336 | url_launcher: 337 | dependency: "direct main" 338 | description: 339 | name: url_launcher 340 | sha256: "781bd58a1eb16069412365c98597726cd8810ae27435f04b3b4d3a470bacd61e" 341 | url: "https://pub.dev" 342 | source: hosted 343 | version: "6.1.12" 344 | url_launcher_android: 345 | dependency: transitive 346 | description: 347 | name: url_launcher_android 348 | sha256: "9e262cbec69233717d5198f4d0b0c4961fa027e3685ba425442c43c64f38bb9b" 349 | url: "https://pub.dev" 350 | source: hosted 351 | version: "6.0.19" 352 | url_launcher_ios: 353 | dependency: transitive 354 | description: 355 | name: url_launcher_ios 356 | sha256: "9af7ea73259886b92199f9e42c116072f05ff9bea2dcb339ab935dfc957392c2" 357 | url: "https://pub.dev" 358 | source: hosted 359 | version: "6.1.4" 360 | url_launcher_linux: 361 | dependency: transitive 362 | description: 363 | name: url_launcher_linux 364 | sha256: "86f3f393cde6bed2a05bfc7f05e52aeaf4f9911a3ad9ff78a42e89e57e5a264a" 365 | url: "https://pub.dev" 366 | source: hosted 367 | version: "2.0.0" 368 | url_launcher_macos: 369 | dependency: transitive 370 | description: 371 | name: url_launcher_macos 372 | sha256: f72b523da791d519aed53c12fd99c7dc50fdd1e4913da904081f3666d06334b5 373 | url: "https://pub.dev" 374 | source: hosted 375 | version: "2.0.0" 376 | url_launcher_platform_interface: 377 | dependency: transitive 378 | description: 379 | name: url_launcher_platform_interface 380 | sha256: bfdfa402f1f3298637d71ca8ecfe840b4696698213d5346e9d12d4ab647ee2ea 381 | url: "https://pub.dev" 382 | source: hosted 383 | version: "2.1.3" 384 | url_launcher_web: 385 | dependency: transitive 386 | description: 387 | name: url_launcher_web 388 | sha256: "057e3458dfcc4276d171ae70cd98efc6d2485bf39b93a015349754e5dec0f657" 389 | url: "https://pub.dev" 390 | source: hosted 391 | version: "2.0.0" 392 | url_launcher_windows: 393 | dependency: transitive 394 | description: 395 | name: url_launcher_windows 396 | sha256: f98b970a12236957881fa28df0c6700f03d9dc5f471cf5ca6d205b77e7ad92d2 397 | url: "https://pub.dev" 398 | source: hosted 399 | version: "2.0.0" 400 | vector_math: 401 | dependency: transitive 402 | description: 403 | name: vector_math 404 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 405 | url: "https://pub.dev" 406 | source: hosted 407 | version: "2.1.4" 408 | win32: 409 | dependency: transitive 410 | description: 411 | name: win32 412 | sha256: f2add6fa510d3ae152903412227bda57d0d5a8da61d2c39c1fb022c9429a41c0 413 | url: "https://pub.dev" 414 | source: hosted 415 | version: "5.0.6" 416 | xdg_directories: 417 | dependency: transitive 418 | description: 419 | name: xdg_directories 420 | sha256: "0186b3f2d66be9a12b0295bddcf8b6f8c0b0cc2f85c6287344e2a6366bc28457" 421 | url: "https://pub.dev" 422 | source: hosted 423 | version: "0.2.0" 424 | sdks: 425 | dart: ">=3.0.0 <4.0.0" 426 | flutter: ">=3.10.0" 427 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: widget_with_codeview 2 | description: A widget with side-by-side source code view. 3 | version: 3.1.0 4 | homepage: https://github.com/X-Wei/widget_with_codeview 5 | 6 | environment: 7 | sdk: '>=2.17.0 <4.0.0' 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | flutter_highlight: ^0.7.0 13 | flutter_speed_dial: ^7.0.0 14 | google_fonts: ^5.1.0 15 | selectable: ^0.3.0 16 | string_scanner: ^1.2.0 17 | url_launcher: ^6.1.7 18 | 19 | dev_dependencies: 20 | flutter_test: 21 | sdk: flutter 22 | 23 | # For information on the generic Dart part of this file, see the 24 | # following page: https://dart.dev/tools/pub/pubspec 25 | # The following section is specific to Flutter. 26 | flutter: null 27 | --------------------------------------------------------------------------------