Setup simple devtools extension

This commit is contained in:
Simon Binder 2023-10-16 23:38:35 +02:00
parent 2582109614
commit 7b27d21755
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
25 changed files with 951 additions and 1 deletions

View File

@ -0,0 +1,4 @@
name: drift
issue_tracker: https://github.com/simolus3/drift/issues
version: 0.0.1
material_icon_code_point: '0xf41e'

View File

@ -61,13 +61,18 @@ abstract class GeneratedDatabase extends DatabaseConnectionUser
/// Used by generated code
GeneratedDatabase(QueryExecutor executor, {StreamQueryStore? streamStore})
: super(executor, streamQueries: streamStore) {
assert(_handleInstantiated());
_whenConstructed();
}
/// Used by generated code to connect to a database that is already open.
GeneratedDatabase.connect(DatabaseConnection connection)
: super.fromConnection(connection) {
_whenConstructed();
}
void _whenConstructed() {
assert(_handleInstantiated());
devtools.handleCreated(this);
}
bool _handleInstantiated() {

View File

@ -6,6 +6,8 @@ import 'package:drift/src/runtime/executor/stream_queries.dart';
import 'package:drift/src/runtime/executor/transactions.dart';
import 'package:meta/meta.dart';
import '../devtools/devtools.dart' as devtools;
part 'batch.dart';
part 'connection.dart';
part 'connection_user.dart';

View File

@ -0,0 +1,50 @@
// This file must not be moved, as the devtools extension will try to look up
// types in this exact library.
// ignore_for_file: public_member_api_docs
@internal
library;
import 'dart:convert';
import 'dart:developer' as developer;
import 'package:meta/meta.dart';
import '../api/runtime_api.dart';
import 'shared.dart';
const _releaseMode = bool.fromEnvironment('dart.vm.product');
const _profileMode = bool.fromEnvironment('dart.vm.profile');
const _enable = !_releaseMode && !_profileMode;
void _postEvent(String type, Map<Object?, Object?> data) {
developer.postEvent('drift:$type', data);
}
void _postChangedEvent() {
_postEvent('database-list-changed', {});
}
class TrackedDatabase {
final GeneratedDatabase database;
final int id;
TrackedDatabase(this.database) : id = _nextId++ {
byDatabase[database] = this;
all.add(this);
}
static int _nextId = 0;
static List<TrackedDatabase> all = [];
static final Expando<TrackedDatabase> byDatabase = Expando();
}
void handleCreated(GeneratedDatabase database) {
if (_enable) {
TrackedDatabase(database);
_postChangedEvent();
}
}
String describe(GeneratedDatabase database) {
return json.encode(DatabaseDescription.fromDrift(database));
}

View File

@ -0,0 +1,122 @@
// ignore_for_file: public_member_api_docs
@internal
library;
import 'package:json_annotation/json_annotation.dart';
import 'package:meta/meta.dart';
import '../api/runtime_api.dart';
import '../query_builder/query_builder.dart';
import '../types/mapping.dart';
part 'shared.g.dart';
typedef JsonObject = Map<String, Object?>;
@JsonSerializable()
class TypeDescription {
final DriftSqlType? type;
final String? customTypeName;
TypeDescription({this.type, this.customTypeName});
factory TypeDescription.fromDrift(GenerationContext ctx, BaseSqlType type) {
return switch (type) {
DriftSqlType() => TypeDescription(type: type),
CustomSqlType<Object>() =>
TypeDescription(customTypeName: type.sqlTypeName(ctx)),
};
}
factory TypeDescription.fromJson(JsonObject obj) =>
_$TypeDescriptionFromJson(obj);
JsonObject toJson() => _$TypeDescriptionToJson(this);
}
@JsonSerializable()
class ColumnDescription {
final String name;
final TypeDescription? type;
final bool isNullable;
ColumnDescription(
{required this.name, required this.type, required this.isNullable});
factory ColumnDescription.fromDrift(
GenerationContext ctx, GeneratedColumn column) {
return ColumnDescription(
name: column.name,
type: TypeDescription.fromDrift(ctx, column.type),
isNullable: column.$nullable,
);
}
factory ColumnDescription.fromJson(JsonObject obj) =>
_$ColumnDescriptionFromJson(obj);
JsonObject toJson() => _$ColumnDescriptionToJson(this);
}
@JsonSerializable()
class EntityDescription {
final String name;
final String type;
final List<ColumnDescription>? columns;
EntityDescription(
{required this.name, required this.type, required this.columns});
factory EntityDescription.fromDrift(
GenerationContext ctx, DatabaseSchemaEntity entity) {
return EntityDescription(
name: entity.entityName,
type: switch (entity) {
TableInfo() => 'table',
ViewInfo() => 'view',
Index() => 'index',
Trigger() => 'trigger',
_ => 'unknown',
},
columns: switch (entity) {
ResultSetImplementation() => [
for (final column in entity.$columns)
ColumnDescription.fromDrift(ctx, column),
],
_ => null,
},
);
}
factory EntityDescription.fromJson(JsonObject obj) =>
_$EntityDescriptionFromJson(obj);
JsonObject toJson() => _$EntityDescriptionToJson(this);
}
@JsonSerializable()
class DatabaseDescription {
final bool dateTimeAsText;
final List<EntityDescription> entities;
DatabaseDescription({required this.dateTimeAsText, required this.entities});
factory DatabaseDescription.fromDrift(GeneratedDatabase database) {
final context = GenerationContext.fromDb(database);
return DatabaseDescription(
dateTimeAsText: database.options
.createTypeMapping(SqlDialect.sqlite)
.storeDateTimesAsText,
entities: [
for (final entity in database.allSchemaEntities)
EntityDescription.fromDrift(context, entity),
],
);
}
factory DatabaseDescription.fromJson(JsonObject obj) =>
_$DatabaseDescriptionFromJson(obj);
JsonObject toJson() => _$DatabaseDescriptionToJson(this);
}

View File

@ -0,0 +1,77 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'shared.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
TypeDescription _$TypeDescriptionFromJson(Map<String, dynamic> json) =>
TypeDescription(
type: $enumDecodeNullable(_$DriftSqlTypeEnumMap, json['type']),
customTypeName: json['customTypeName'] as String?,
);
Map<String, dynamic> _$TypeDescriptionToJson(TypeDescription instance) =>
<String, dynamic>{
'type': _$DriftSqlTypeEnumMap[instance.type],
'customTypeName': instance.customTypeName,
};
const _$DriftSqlTypeEnumMap = {
DriftSqlType.bool: 'bool',
DriftSqlType.string: 'string',
DriftSqlType.bigInt: 'bigInt',
DriftSqlType.int: 'int',
DriftSqlType.dateTime: 'dateTime',
DriftSqlType.blob: 'blob',
DriftSqlType.double: 'double',
DriftSqlType.any: 'any',
};
ColumnDescription _$ColumnDescriptionFromJson(Map<String, dynamic> json) =>
ColumnDescription(
name: json['name'] as String,
type: json['type'] == null
? null
: TypeDescription.fromJson(json['type'] as Map<String, dynamic>),
isNullable: json['isNullable'] as bool,
);
Map<String, dynamic> _$ColumnDescriptionToJson(ColumnDescription instance) =>
<String, dynamic>{
'name': instance.name,
'type': instance.type,
'isNullable': instance.isNullable,
};
EntityDescription _$EntityDescriptionFromJson(Map<String, dynamic> json) =>
EntityDescription(
name: json['name'] as String,
type: json['type'] as String,
columns: (json['columns'] as List<dynamic>?)
?.map((e) => ColumnDescription.fromJson(e as Map<String, dynamic>))
.toList(),
);
Map<String, dynamic> _$EntityDescriptionToJson(EntityDescription instance) =>
<String, dynamic>{
'name': instance.name,
'type': instance.type,
'columns': instance.columns,
};
DatabaseDescription _$DatabaseDescriptionFromJson(Map<String, dynamic> json) =>
DatabaseDescription(
dateTimeAsText: json['dateTimeAsText'] as bool,
entities: (json['entities'] as List<dynamic>)
.map((e) => EntityDescription.fromJson(e as Map<String, dynamic>))
.toList(),
);
Map<String, dynamic> _$DatabaseDescriptionToJson(
DatabaseDescription instance) =>
<String, dynamic>{
'dateTimeAsText': instance.dateTimeAsText,
'entities': instance.entities,
};

View File

@ -13,6 +13,7 @@ dependencies:
convert: ^3.0.0
collection: ^1.15.0
js: ^0.6.3
json_annotation: ^4.8.1
meta: ^1.3.0
stream_channel: ^2.1.0
sqlite3: ^2.0.0
@ -24,6 +25,7 @@ dev_dependencies:
build_runner_core: ^7.0.0
build_verify: ^3.0.0
build_web_compilers: ^4.0.3
json_serializable: ^6.7.1
drift_dev: any
drift_testcases:
path: ../extras/integration_tests/drift_testcases

View File

@ -0,0 +1,44 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release

View File

@ -0,0 +1,30 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a
base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a
- platform: web
create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a
base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

View File

@ -0,0 +1,10 @@
This is the [DevTools extension](https://pub.dev/packages/devtools_extensions) for drift,
based on the [drift_db_viewer](https://pub.dev/packages/drift_db_viewer) package.
## Debugging
To run the extension in standalone mode, run
```
flutter run -d chrome --dart-define=use_simulated_environment=true
```

View File

@ -0,0 +1,28 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View File

@ -0,0 +1,61 @@
import 'package:devtools_app_shared/ui.dart';
import 'package:devtools_extensions/devtools_extensions.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'src/details.dart';
import 'src/list.dart';
void main() {
runApp(const ProviderScope(child: DriftDevToolsExtension()));
}
class DriftDevToolsExtension extends StatelessWidget {
const DriftDevToolsExtension({super.key});
@override
Widget build(BuildContext context) {
return const DevToolsExtension(child: DriftDevtoolsBody());
}
}
class DriftDevtoolsBody extends ConsumerWidget {
const DriftDevtoolsBody({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final selected = ref.watch(selectedDatabase);
return Split(
axis: Split.axisFor(context, 0.85),
initialFractions: const [1 / 3, 2 / 3],
children: [
const RoundedOutlinedBorder(
child: Column(
children: [
AreaPaneHeader(
roundedTopBorder: false,
includeTopBorder: false,
title: Text('Drift databases'),
),
Expanded(child: DatabaseList()),
],
),
),
RoundedOutlinedBorder(
clip: true,
child: Column(children: [
AreaPaneHeader(
roundedTopBorder: false,
includeTopBorder: false,
title: selected != null
? Text(selected.typeName)
: const Text('No database selected'),
),
if (selected != null) const Expanded(child: DatabaseDetails())
]),
),
],
);
}
}

View File

@ -0,0 +1,71 @@
import 'package:devtools_app_shared/service.dart';
import 'package:devtools_extensions/devtools_extensions.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:vm_service/vm_service.dart';
import 'list.dart';
import 'remote_database.dart';
import 'service.dart';
final loadedDatabase = AutoDisposeFutureProvider((ref) async {
final selected = ref.watch(selectedDatabase);
final eval = await ref.watch(driftEvalProvider.future);
final isAlive = Disposable();
ref.onDispose(isAlive.dispose);
if (selected?.database case InstanceRef dbRef) {
final db = await eval.safeGetInstance(dbRef, isAlive);
return await RemoteDatabase.resolve(db, eval, isAlive);
}
return null;
});
class DatabaseDetails extends ConsumerStatefulWidget {
const DatabaseDetails({super.key});
@override
ConsumerState<ConsumerStatefulWidget> createState() {
return _DatabaseDetailsState();
}
}
class _DatabaseDetailsState extends ConsumerState<DatabaseDetails> {
final ScrollController controller = ScrollController();
@override
void dispose() {
super.dispose();
controller.dispose();
}
@override
Widget build(BuildContext context) {
final database = ref.watch(loadedDatabase);
return database.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (err, stack) => Text('unknown error: $err\n$stack'),
data: (database) {
if (database != null) {
return Scrollbar(
controller: controller,
child: ListView(
controller: controller,
children: [
for (final entity in database.description.entities)
Text('${entity.name}: ${entity.type}'),
],
),
);
} else {
return const SizedBox.shrink();
}
},
);
}
}

View File

@ -0,0 +1,171 @@
import 'package:devtools_app_shared/service.dart';
import 'package:devtools_app_shared/ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:rxdart/transformers.dart';
import 'package:vm_service/vm_service.dart';
import 'package:path/path.dart' as p;
import 'service.dart';
class TrackedDatabase {
final int id;
final InstanceRef database;
TrackedDatabase({required this.id, required this.database});
String get typeName => database.classRef!.name!;
}
final _databaseListChanged = AutoDisposeStreamProvider<void>((ref) {
return Stream.fromFuture(ref.watch(serviceProvider.future))
.switchMap((serviceProvider) {
return serviceProvider.onExtensionEvent.where((event) {
return event.extensionKind == 'drift:database-list-changed';
});
});
});
final databaseList =
AutoDisposeFutureProvider<List<TrackedDatabase>>((ref) async {
ref
..watch(hotRestartEventProvider)
..watch(_databaseListChanged);
final isAlive = Disposable();
ref.onDispose(isAlive.dispose);
final eval = await ref.watch(driftEvalProvider.future);
final resultsRefList =
await eval.evalInstance('TrackedDatabase.all', isAlive: isAlive);
return await Future.wait(
resultsRefList.elements!.cast<InstanceRef>().map((trackedRef) async {
final trackedDatabase = await eval.safeGetInstance(trackedRef, isAlive);
final idField = trackedDatabase.fields!.firstWhere((f) => f.name == 'id');
final databaseField =
trackedDatabase.fields!.firstWhere((f) => f.name == 'database');
final (id, database) = await (
eval.safeGetInstance(idField.value, isAlive),
eval.safeGetInstance(databaseField.value, isAlive)
).wait;
return TrackedDatabase(
id: int.parse(id.valueAsString!),
database: database,
);
}));
});
final selectedDatabase = AutoDisposeStateNotifierProvider<
StateController<TrackedDatabase?>, TrackedDatabase?>((ref) {
final controller = StateController<TrackedDatabase?>(null);
ref.listen(
databaseList,
(previous, next) {
final databases = next.asData?.value ?? const [];
if (databases.isEmpty) {
controller.state = null;
} else if (controller.state == null &&
databases.every((e) => e.id != controller.state?.id)) {
controller.state = databases.first;
}
},
fireImmediately: true,
);
return controller;
});
class DatabaseList extends ConsumerStatefulWidget {
const DatabaseList({super.key});
@override
ConsumerState<ConsumerStatefulWidget> createState() {
return _DatabaseListState();
}
}
class _DatabaseListState extends ConsumerState<DatabaseList> {
static const _tilePadding = EdgeInsets.only(
left: defaultSpacing,
right: densePadding,
top: densePadding,
bottom: densePadding,
);
final scrollController = ScrollController();
@override
void dispose() {
scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final databases = ref.watch(databaseList);
return databases.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (err, stack) => Padding(
padding: _tilePadding,
child: Text('Could not load databases: $err\n$stack'),
),
data: (databases) {
return Scrollbar(
controller: scrollController,
thumbVisibility: true,
child: ListView(
primary: false,
controller: scrollController,
children: [
for (final db in databases) _DatabaseEntry(database: db),
],
),
);
},
);
}
}
class _DatabaseEntry extends ConsumerWidget {
final TrackedDatabase database;
const _DatabaseEntry({super.key, required this.database});
@override
Widget build(BuildContext context, WidgetRef ref) {
final isSelected = ref.watch(selectedDatabase)?.id == database.id;
final colorScheme = Theme.of(context).colorScheme;
String? fileName;
int? lineNumber;
if (database.database.classRef?.location case SourceLocation sl) {
final uri = sl.script?.uri;
if (uri != null) {
fileName = p.url.basename(uri);
}
lineNumber = sl.line;
}
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {},
child: Container(
color: isSelected ? colorScheme.selectedRowBackgroundColor : null,
padding: _DatabaseListState._tilePadding,
child: ListTile(
title: Text(database.typeName),
subtitle: fileName != null && lineNumber != null
? Text('$fileName:$lineNumber')
: null,
),
),
);
}
}

View File

@ -0,0 +1,30 @@
import 'dart:convert';
import 'package:devtools_app_shared/service.dart';
// ignore: invalid_use_of_internal_member, implementation_imports
import 'package:drift/src/runtime/devtools/shared.dart';
import 'package:vm_service/vm_service.dart';
/// Utilities to access a drift database via service extensions.
class RemoteDatabase {
final DatabaseDescription description;
RemoteDatabase({required this.description});
static Future<RemoteDatabase> resolve(
Instance database,
EvalOnDartLibrary eval,
Disposable isAlive,
) async {
final stringVal = await eval.evalInstance(
'describe(db)',
isAlive: isAlive,
scope: {'db': database.id!},
);
final value = await eval.retrieveFullValueAsString(stringVal);
final description = DatabaseDescription.fromJson(json.decode(value!));
return RemoteDatabase(description: description);
}
}

View File

@ -0,0 +1,50 @@
import 'dart:async';
import 'package:devtools_app_shared/service.dart';
import 'package:devtools_extensions/devtools_extensions.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:rxdart/transformers.dart';
import 'package:vm_service/vm_service.dart';
final _serviceConnection = StreamController<VmService>.broadcast();
void setServiceConnectionForProviderScreen(VmService service) {
_serviceConnection.add(service);
}
final serviceProvider = StreamProvider<VmService>((ref) {
return _serviceConnection.stream.startWith(serviceManager.service!);
});
final _libraryEvalProvider =
FutureProviderFamily<EvalOnDartLibrary, String>((ref, libraryPath) async {
final service = await ref.watch(serviceProvider.future);
final eval = EvalOnDartLibrary(
libraryPath,
service,
serviceManager: serviceManager,
);
ref.onDispose(eval.dispose);
return eval;
});
final driftEvalProvider =
_libraryEvalProvider('package:drift/src/runtime/devtools/devtools.dart');
final hotRestartEventProvider =
ChangeNotifierProvider<ValueNotifier<void>>((ref) {
final selectedIsolateListenable =
serviceManager.isolateManager.selectedIsolate;
// Since ChangeNotifierProvider calls `dispose` on the returned ChangeNotifier
// when the provider is destroyed, we can't simply return `selectedIsolateListenable`.
// So we're making a copy of it instead.
final notifier = ValueNotifier<IsolateRef?>(selectedIsolateListenable.value);
void listener() => notifier.value = selectedIsolateListenable.value;
selectedIsolateListenable.addListener(listener);
ref.onDispose(() => selectedIsolateListenable.removeListener(listener));
return notifier;
});

View File

@ -0,0 +1,98 @@
name: drift_devtools_extension
description: A new Flutter project.
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1
environment:
sdk: '>=3.1.0 <4.0.0'
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
devtools_extensions: ^0.0.8
devtools_app_shared: ^0.0.5
db_viewer: ^1.0.3
rxdart: ^0.27.7
flutter_riverpod: ^2.4.4
vm_service: ^11.10.0
path: ^1.8.3
drift: ^2.12.1
dev_dependencies:
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^2.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -0,0 +1,59 @@
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="drift_devtools_extension">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>drift_devtools_extension</title>
<link rel="manifest" href="manifest.json">
<script>
// The value below is injected by flutter build, do not touch.
const serviceWorkerVersion = null;
</script>
<!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script>
</head>
<body>
<script>
window.addEventListener('load', function(ev) {
// Download main.dart.js
_flutter.loader.loadEntrypoint({
serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion,
},
onEntrypointLoaded: function(engineInitializer) {
engineInitializer.initializeEngine().then(function(appRunner) {
appRunner.runApp();
});
}
});
});
</script>
</body>
</html>

View File

@ -0,0 +1,35 @@
{
"name": "drift_devtools_extension",
"short_name": "drift_devtools_extension",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "A new Flutter project.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "icons/Icon-maskable-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "icons/Icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}

View File

@ -9,6 +9,7 @@ packages:
- sqlparser
- examples/*
- extras/benchmarks
- extras/drift_devtools_extension
- extras/drift_mariadb
- extras/drift_postgres
- extras/encryption