Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add skeleton for extension screen views and controllers #6054

Merged
merged 7 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/devtools_app/lib/src/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import 'package:provider/provider.dart';

import 'example/conditional_screen.dart';
import 'extensions/extension_model.dart';
import 'extensions/ui/extension_screen.dart';
import 'extensions/extension_screen.dart';
import 'framework/framework_core.dart';
import 'framework/home_screen.dart';
import 'framework/initializer.dart';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright 2023 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'controller.dart';

class EmbeddedExtensionControllerImpl extends EmbeddedExtensionController {
// TODO(kenz): implement desktop stubs that match the web implementation.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright 2023 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'controller.dart';

class EmbeddedExtensionControllerImpl extends EmbeddedExtensionController {
// TODO(kenz): implement web controller for embedded extension
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2023 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';

import 'controller.dart';

class EmbeddedExtension extends StatelessWidget {
const EmbeddedExtension({super.key, required this.controller});

final EmbeddedExtensionController controller;

@override
Widget build(BuildContext context) {
// TODO(kenz): if web view support for desktop is ever added, use that here.
return const Center(
child: Text(
'Cannot display the DevTools plugin.'
' IFrames are not supported on desktop platforms.',
),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2023 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';

import 'controller.dart';

class EmbeddedExtension extends StatelessWidget {
const EmbeddedExtension({super.key, required this.controller});

final EmbeddedExtensionController controller;

@override
Widget build(BuildContext context) {
return const Center(
child: Text('TODO implement embedded extension web view'),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright 2023 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import '../../shared/primitives/auto_dispose.dart';
import '_controller_desktop.dart' if (dart.library.html) '_controller_web.dart';

EmbeddedExtensionControllerImpl createEmbeddedExtensionController() {
return EmbeddedExtensionControllerImpl();
}

abstract class EmbeddedExtensionController extends DisposableController {}
27 changes: 27 additions & 0 deletions packages/devtools_app/lib/src/extensions/embedded/view.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2023 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';

import '_view_desktop.dart' if (dart.library.html) '_view_web.dart';
import 'controller.dart';

/// A widget that displays a DevTools extension in an embedded iFrame.
///
/// A DevTools extension is provided by a pub package and is served by the
/// DevTools server when present for a connected application.
///
/// When DevTools is run on Desktop for development, this widget displays a
/// placeholder, since Flutter Desktop does not currently support web views.
class EmbeddedExtensionView extends StatelessWidget {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doc comment?
Can you explain or reference explanation of difference between embedded and non-embedded extension?

const EmbeddedExtensionView({Key? key, required this.controller})
: super(key: key);

final EmbeddedExtensionController controller;

@override
Widget build(BuildContext context) {
return EmbeddedExtension(controller: controller);
}
}
146 changes: 146 additions & 0 deletions packages/devtools_app/lib/src/extensions/extension_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// Copyright 2023 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

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

import '../shared/analytics/constants.dart' as gac;
import '../shared/common_widgets.dart';
import '../shared/primitives/listenable.dart';
import '../shared/primitives/utils.dart';
import '../shared/screen.dart';
import '../shared/theme.dart';
import 'embedded/controller.dart';
import 'embedded/view.dart';
import 'extension_model.dart';

class ExtensionScreen extends Screen {
ExtensionScreen(this.extensionConfig)
: super.conditional(
// TODO(kenz): we may need to ensure this is a unique id.
id: '${extensionConfig.name}-ext',
title: extensionConfig.name.toSentenceCase(),
icon: extensionConfig.icon,
// TODO(kenz): support static DevTools extensions.
requiresConnection: true,
);

final DevToolsExtensionConfig extensionConfig;

@override
ValueListenable<bool> get showIsolateSelector =>
const FixedValueListenable<bool>(true);

@override
Widget build(BuildContext context) =>
_ExtensionScreenBody(extensionConfig: extensionConfig);
}

class _ExtensionScreenBody extends StatefulWidget {
const _ExtensionScreenBody({required this.extensionConfig});

final DevToolsExtensionConfig extensionConfig;

@override
State<_ExtensionScreenBody> createState() => __ExtensionScreenBodyState();
}

class __ExtensionScreenBodyState extends State<_ExtensionScreenBody> {
late final EmbeddedExtensionController extensionController;

@override
void initState() {
super.initState();
extensionController = createEmbeddedExtensionController();
}

@override
void dispose() {
extensionController.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return ExtensionView(
controller: extensionController,
extension: widget.extensionConfig,
);
}
}

class ExtensionView extends StatelessWidget {
const ExtensionView({
super.key,
required this.controller,
required this.extension,
});

final EmbeddedExtensionController controller;

final DevToolsExtensionConfig extension;

@override
Widget build(BuildContext context) {
return RoundedOutlinedBorder(
clip: true,
child: Column(
children: [
EmbeddedExtensionHeader(extension: extension),
Expanded(
child: KeepAliveWrapper(
child: Center(
child: EmbeddedExtensionView(controller: controller),
),
),
),
],
),
);
}
}

// TODO(kenz): add button to deactivate extension once activate / deactivate
// logic is hooked up.
class EmbeddedExtensionHeader extends StatelessWidget {
const EmbeddedExtensionHeader({super.key, required this.extension});

final DevToolsExtensionConfig extension;

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final extensionName = extension.name.toLowerCase();
return AreaPaneHeader(
title: RichText(
text: TextSpan(
text: 'package:$extensionName extension',
style: theme.regularTextStyle.copyWith(fontWeight: FontWeight.bold),
children: [
TextSpan(
text: ' (v${extension.version})',
style: theme.subtleTextStyle,
),
],
),
),
includeTopBorder: false,
roundedTopBorder: false,
rightPadding: defaultSpacing,
actions: [
RichText(
text: LinkTextSpan(
link: Link(
display: 'Report an issue',
url: extension.issueTrackerLink,
gaScreenName: gac.extensionScreenId,
gaSelectedItemDescription: gac.extensionFeedback(extensionName),
),
context: context,
),
),
],
);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ const feedbackButton = 'feedbackButton';
const contributingLink = 'contributing';
const discordLink = 'discord';

// Extension screens UX actions.
const extensionScreenId = 'devtoolsExtension';
String extensionFeedback(String name) => 'extensionFeedback-$name';

// Inspector UX actions:
const refresh = 'refresh';
const refreshEmptyTree = 'refreshEmptyTree';
Expand Down
2 changes: 1 addition & 1 deletion packages/devtools_app/lib/src/shared/screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ abstract class Screen {
text: TextSpan(text: title),
textDirection: TextDirection.ltr,
)..layout();
const measurementBuffer = 1.5;
const measurementBuffer = 2.0;
return painter.width +
denseSpacing +
defaultIconSize +
Expand Down
Loading