blob: 699c150102f9ebc975507c45868638dd10201bad [file]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// To run this, from the root of the Flutter repository:
// bin/cache/dart-sdk/bin/dart --enable-asserts dev/bots/check_examples_cross_imports.dart
import 'dart:io';
import 'package:args/args.dart';
import 'package:file/file.dart';
import 'package:file/local.dart';
import 'package:path/path.dart' as path;
import 'cross_imports_checker_utils.dart';
import 'utils.dart';
final String _scriptLocation = path.fromUri(Platform.script);
final String _flutterRoot = path.dirname(path.dirname(path.dirname(_scriptLocation)));
final String _examplesDirectoryPath = path.join(_flutterRoot, 'examples');
void main(List<String> args) {
final argParser = ArgParser();
argParser.addFlag('help', negatable: false, help: 'Print help for this command.');
argParser.addOption(
'examples',
valueHelp: 'path',
defaultsTo: _examplesDirectoryPath,
help: 'A location where the examples are found.',
);
argParser.addOption(
'flutter-root',
valueHelp: 'path',
defaultsTo: _flutterRoot,
help: 'The path to the root of the Flutter repo.',
);
final ArgResults parsedArgs;
void usage() {
print('dart --enable-asserts ${path.basename(_scriptLocation)} [options]');
print(argParser.usage);
}
try {
parsedArgs = argParser.parse(args);
} on FormatException catch (e) {
print(e.message);
usage();
exit(1);
}
if (parsedArgs['help'] as bool) {
usage();
exit(0);
}
const FileSystem filesystem = LocalFileSystem();
final Directory examplesDirectory = filesystem.directory(parsedArgs['examples']! as String);
final Directory flutterRoot = filesystem.directory(parsedArgs['flutter-root']! as String);
final checker = ExamplesCrossImportChecker(
examplesDirectory: examplesDirectory,
flutterRoot: flutterRoot,
);
if (!checker.check()) {
reportErrorsAndExit('Some errors were found in the examples imports.');
}
reportSuccessAndExit('No errors were detected with examples cross imports.');
}
/// Checks the examples in `examples/**` libraries for cross imports.
///
/// Excludes known examples that contain cross imports, i.e.
/// [ExamplesCrossImportChecker.knownExamplesFlutterViewCrossImports] and
/// [ExamplesCrossImportChecker.knownExamplesImageListCrossImports].
///
/// No examples should import Material or Cupertino.
/// Any Material or Cupertino specific examples should go in
/// packages/material_ui or packages/cupertino_ui respectively.
class ExamplesCrossImportChecker {
ExamplesCrossImportChecker({
required this.examplesDirectory,
required this.flutterRoot,
this.filesystem = const LocalFileSystem(),
});
final Directory examplesDirectory;
final Directory flutterRoot;
final FileSystem filesystem;
static const String _kSampleTemplatesDirectoryName = 'sample_templates';
/// The known cross imports in the `examples/` directory, including subdirectories.
///
/// These cross imports should all eventually be resolved, but until they are we allow them, so
/// that we can catch any new cross imports that are added.
// TODO(justinmc): Fix all of these tests so there are no cross imports.
// See https://github.com/flutter/flutter/issues/187645.
static final Set<String> knownExamplesCrossImports = <String>{
'examples/api/lib/animation/animation_controller/animated_digit.0.dart',
'examples/api/lib/animation/curves/curve2_d.0.dart',
'examples/api/test/animation/animation_controller/animated_digit.0_test.dart',
'examples/api/test/animation/curves/curve2_d.0_test.dart',
'examples/api/lib/foundation/key/value_key.0.dart',
'examples/api/test/foundation/key/value_key.0_test.dart',
'examples/api/lib/gestures/pointer_signal_resolver/pointer_signal_resolver.0.dart',
'examples/api/lib/gestures/tap_and_drag/tap_and_drag.0.dart',
'examples/api/test/gestures/pointer_signal_resolver/pointer_signal_resolver.0_test.dart',
'examples/api/test/gestures/tap_and_drag/tap_and_drag.0_test.dart',
'examples/api/lib/painting/gradient/linear_gradient.0.dart',
'examples/api/lib/painting/star_border/star_border.0.dart',
'examples/api/lib/painting/axis_direction/axis_direction.0.dart',
'examples/api/lib/painting/borders/border_side.stroke_align.0.dart',
'examples/api/lib/painting/linear_border/linear_border.0.dart',
'examples/api/lib/painting/image_provider/image_provider.0.dart',
'examples/api/lib/painting/rounded_superellipse_border/rounded_superellipse_border.0.dart',
'examples/api/test/painting/gradient/linear_gradient.0_test.dart',
'examples/api/test/painting/star_border/star_border.0_test.dart',
'examples/api/test/painting/axis_direction/axis_direction.0_test.dart',
'examples/api/test/painting/borders/border_side.stroke_align.0_test.dart',
'examples/api/test/painting/linear_border/linear_border.0_test.dart',
'examples/api/test/painting/rounded_superellipse_border/rounded_superellipse_border.0_test.dart',
'examples/api/lib/rendering/sliver_grid/sliver_grid_delegate_with_fixed_cross_axis_count.0.dart',
'examples/api/lib/rendering/sliver_grid/sliver_grid_delegate_with_fixed_cross_axis_count.1.dart',
'examples/api/lib/rendering/growth_direction/growth_direction.0.dart',
'examples/api/lib/rendering/box/parent_data.0.dart',
'examples/api/lib/rendering/scroll_direction/scroll_direction.0.dart',
'examples/api/test/rendering/sliver_grid/sliver_grid_delegate_with_fixed_cross_axis_count.1_test.dart',
'examples/api/test/rendering/sliver_grid/sliver_grid_delegate_with_fixed_cross_axis_count.0_test.dart',
'examples/api/test/rendering/growth_direction/growth_direction.0_test.dart',
'examples/api/test/rendering/box/parent_data.0_test.dart',
'examples/api/test/rendering/scroll_direction/scroll_direction.0_test.dart',
'examples/api/lib/services/mouse_cursor/mouse_cursor.0.dart',
'examples/api/lib/services/binding/handle_request_app_exit.0.dart',
'examples/api/lib/services/text_input/text_input_control.0.dart',
'examples/api/lib/services/keyboard_key/physical_keyboard_key.0.dart',
'examples/api/lib/services/keyboard_key/logical_keyboard_key.0.dart',
'examples/api/lib/services/system_chrome/system_chrome.set_system_u_i_overlay_style.0.dart',
'examples/api/lib/services/system_chrome/system_chrome.set_system_u_i_overlay_style.1.dart',
'examples/api/test/services/text_input/text_input_control.0_test.dart',
'examples/api/test/services/system_chrome/system_chrome.set_system_u_i_overlay_style.0_test.dart',
'examples/api/test/services/system_chrome/system_chrome.set_system_u_i_overlay_style.1_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_ordinal_forms.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_contextual_alternates.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_alternative_fractions.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_oldstyle_figures.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_stylistic_alternates.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_historical_forms.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_stylistic_set.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_slashed_zero.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_superscripts.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_case_sensitive_forms.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_proportional_figures.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_historical_ligatures.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_tabular_figures.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_notational_forms.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_stylistic_set.1_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_alternative.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_locale_aware.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_fractions.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_denominator.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_lining_figures.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_numerators.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_swash.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_scientific_inferiors.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_subscripts.0_test.dart',
'examples/api/test/ui/text/font_feature.font_feature_character_variant.0_test.dart',
'examples/api/lib/widgets/animated_grid/sliver_animated_grid.0.dart',
'examples/api/lib/widgets/animated_grid/animated_grid.0.dart',
'examples/api/lib/widgets/navigator_pop_handler/navigator_pop_handler.1.dart',
'examples/api/lib/widgets/navigator_pop_handler/navigator_pop_handler.0.dart',
'examples/api/lib/widgets/editable_text/editable_text.on_changed.0.dart',
'examples/api/lib/widgets/editable_text/text_editing_controller.0.dart',
'examples/api/lib/widgets/editable_text/text_editing_controller.1.dart',
'examples/api/lib/widgets/editable_text/editable_text.on_content_inserted.0.dart',
'examples/api/lib/widgets/page/page_can_pop.0.dart',
'examples/api/lib/widgets/undo_history/undo_history_controller.0.dart',
'examples/api/lib/widgets/raw_tooltip/raw_tooltip.0.dart',
'examples/api/lib/widgets/form/form.1.dart',
'examples/api/lib/widgets/form/form.0.dart',
'examples/api/lib/widgets/layout_builder/layout_builder.0.dart',
'examples/api/lib/widgets/tap_region/text_field_tap_region.0.dart',
'examples/api/lib/widgets/restoration/restoration_mixin.0.dart',
'examples/api/lib/widgets/app/widgets_app.widgets_app.0.dart',
'examples/api/lib/widgets/drag_target/draggable.0.dart',
'examples/api/lib/widgets/autocomplete/raw_autocomplete.focus_node.0.dart',
'examples/api/lib/widgets/autocomplete/raw_autocomplete.2.dart',
'examples/api/lib/widgets/autocomplete/raw_autocomplete.1.dart',
'examples/api/lib/widgets/autocomplete/raw_autocomplete.0.dart',
'examples/api/lib/widgets/keep_alive/automatic_keep_alive_client_mixin.0.dart',
'examples/api/lib/widgets/keep_alive/automatic_keep_alive.0.dart',
'examples/api/lib/widgets/keep_alive/keep_alive.0.dart',
'examples/api/lib/widgets/safe_area/safe_area.0.dart',
'examples/api/lib/widgets/scroll_notification_observer/scroll_notification_observer.0.dart',
'examples/api/lib/widgets/animated_size/animated_size.0.dart',
'examples/api/lib/widgets/framework/error_widget.0.dart',
'examples/api/lib/widgets/framework/build_owner.0.dart',
'examples/api/lib/widgets/sliver/pinned_header_sliver.1.dart',
'examples/api/lib/widgets/sliver/sliver_list.0.dart',
'examples/api/lib/widgets/sliver/pinned_header_sliver.0.dart',
'examples/api/lib/widgets/sliver/sliver_constrained_cross_axis.0.dart',
'examples/api/lib/widgets/sliver/sliver_tree.1.dart',
'examples/api/lib/widgets/sliver/sliver_tree.0.dart',
'examples/api/lib/widgets/sliver/sliver_floating_header.0.dart',
'examples/api/lib/widgets/sliver/decorated_sliver.1.dart',
'examples/api/lib/widgets/sliver/sliver_opacity.1.dart',
'examples/api/lib/widgets/sliver/decorated_sliver.0.dart',
'examples/api/lib/widgets/sliver/sliver_ensure_semantics.0.dart',
'examples/api/lib/widgets/sliver/sliver_resizing_header.0.dart',
'examples/api/lib/widgets/sliver/sliver_cross_axis_group.0.dart',
'examples/api/lib/widgets/sliver/sliver_main_axis_group.0.dart',
'examples/api/lib/widgets/heroes/hero.0.dart',
'examples/api/lib/widgets/heroes/hero.1.dart',
'examples/api/lib/widgets/dismissible/dismissible.0.dart',
'examples/api/lib/widgets/overflow_bar/overflow_bar.0.dart',
'examples/api/lib/widgets/preferred_size/preferred_size.0.dart',
'examples/api/lib/widgets/media_query/media_query_data.system_gesture_insets.0.dart',
'examples/api/lib/widgets/focus_traversal/ordered_traversal_policy.0.dart',
'examples/api/lib/widgets/focus_traversal/focus_traversal_group.0.dart',
'examples/api/lib/widgets/value_listenable_builder/value_listenable_builder.0.dart',
'examples/api/lib/widgets/raw_menu_anchor/raw_menu_anchor.1.dart',
'examples/api/lib/widgets/raw_menu_anchor/raw_menu_anchor.0.dart',
'examples/api/lib/widgets/raw_menu_anchor/raw_menu_anchor.3.dart',
'examples/api/lib/widgets/raw_menu_anchor/raw_menu_anchor.2.dart',
'examples/api/lib/widgets/async/stream_builder.0.dart',
'examples/api/lib/widgets/async/future_builder.0.dart',
'examples/api/lib/widgets/shared_app_data/shared_app_data.1.dart',
'examples/api/lib/widgets/shared_app_data/shared_app_data.0.dart',
'examples/api/lib/widgets/animated_list/animated_list_separated.0.dart',
'examples/api/lib/widgets/animated_list/sliver_animated_list.0.dart',
'examples/api/lib/widgets/animated_list/animated_list.0.dart',
'examples/api/lib/widgets/basic/fractionally_sized_box.0.dart',
'examples/api/lib/widgets/basic/physical_shape.0.dart',
'examples/api/lib/widgets/basic/aspect_ratio.2.dart',
'examples/api/lib/widgets/basic/flow.0.dart',
'examples/api/lib/widgets/basic/aspect_ratio.0.dart',
'examples/api/lib/widgets/basic/clip_rrect.0.dart',
'examples/api/lib/widgets/basic/ignore_pointer.0.dart',
'examples/api/lib/widgets/basic/fitted_box.0.dart',
'examples/api/lib/widgets/basic/custom_multi_child_layout.0.dart',
'examples/api/lib/widgets/basic/listener.0.dart',
'examples/api/lib/widgets/basic/clip_rrect.1.dart',
'examples/api/lib/widgets/basic/offstage.0.dart',
'examples/api/lib/widgets/basic/aspect_ratio.1.dart',
'examples/api/lib/widgets/basic/overflowbox.0.dart',
'examples/api/lib/widgets/basic/indexed_stack.0.dart',
'examples/api/lib/widgets/basic/mouse_region.on_exit.1.dart',
'examples/api/lib/widgets/basic/expanded.1.dart',
'examples/api/lib/widgets/basic/mouse_region.0.dart',
'examples/api/lib/widgets/basic/expanded.0.dart',
'examples/api/lib/widgets/basic/mouse_region.on_exit.0.dart',
'examples/api/lib/widgets/basic/absorb_pointer.0.dart',
'examples/api/lib/widgets/single_child_scroll_view/single_child_scroll_view.1.dart',
'examples/api/lib/widgets/single_child_scroll_view/single_child_scroll_view.0.dart',
'examples/api/lib/widgets/scroll_end_notification/scroll_end_notification.1.dart',
'examples/api/lib/widgets/scroll_end_notification/scroll_end_notification.0.dart',
'examples/api/lib/widgets/autofill/autofill_group.0.dart',
'examples/api/lib/widgets/scroll_view/list_view.1.dart',
'examples/api/lib/widgets/scroll_view/list_view.0.dart',
'examples/api/lib/widgets/scroll_view/grid_view.0.dart',
'examples/api/lib/widgets/scroll_view/custom_scroll_view.1.dart',
'examples/api/lib/widgets/binding/widget_binding_observer.0.dart',
'examples/api/lib/widgets/image/image.loading_builder.0.dart',
'examples/api/lib/widgets/image/image.error_builder.0.dart',
'examples/api/lib/widgets/image/image.frame_builder.0.dart',
'examples/api/lib/widgets/app_lifecycle_listener/app_lifecycle_listener.0.dart',
'examples/api/lib/widgets/app_lifecycle_listener/app_lifecycle_listener.1.dart',
'examples/api/lib/widgets/slotted_render_object_widget/slotted_multi_child_render_object_widget_mixin.0.dart',
'examples/api/lib/widgets/color_filter/color_filtered.0.dart',
'examples/api/lib/widgets/implicit_animations/animated_padding.0.dart',
'examples/api/lib/widgets/implicit_animations/animated_positioned.0.dart',
'examples/api/lib/widgets/implicit_animations/animated_align.0.dart',
'examples/api/lib/widgets/implicit_animations/animated_slide.0.dart',
'examples/api/lib/widgets/implicit_animations/animated_container.0.dart',
'examples/api/lib/widgets/implicit_animations/sliver_animated_opacity.0.dart',
'examples/api/lib/widgets/implicit_animations/animated_fractionally_sized_box.0.dart',
'examples/api/lib/widgets/radio_group/radio_group.0.dart',
'examples/api/lib/widgets/inherited_theme/inherited_theme.0.dart',
'examples/api/lib/widgets/scroll_position/scroll_metrics_notification.0.dart',
'examples/api/lib/widgets/scroll_position/scroll_controller_on_attach.0.dart',
'examples/api/lib/widgets/scroll_position/is_scrolling_listener.0.dart',
'examples/api/lib/widgets/scroll_position/scroll_controller_notification.0.dart',
'examples/api/lib/widgets/page_transitions_builder/page_transitions_builder.0.dart',
'examples/api/lib/widgets/page_storage/page_storage.0.dart',
'examples/api/lib/widgets/table/table.0.dart',
'examples/api/lib/widgets/notification_listener/notification.0.dart',
'examples/api/lib/widgets/inherited_model/inherited_model.0.dart',
'examples/api/lib/widgets/focus_scope/focus.2.dart',
'examples/api/lib/widgets/focus_scope/focus_scope.0.dart',
'examples/api/lib/widgets/focus_scope/focus.1.dart',
'examples/api/lib/widgets/focus_scope/focus.0.dart',
'examples/api/lib/widgets/pop_scope/pop_scope.1.dart',
'examples/api/lib/widgets/pop_scope/pop_scope.0.dart',
'examples/api/lib/widgets/animated_switcher/animated_switcher.0.dart',
'examples/api/lib/widgets/shortcuts/shortcuts.0.dart',
'examples/api/lib/widgets/shortcuts/character_activator.0.dart',
'examples/api/lib/widgets/shortcuts/shortcuts.1.dart',
'examples/api/lib/widgets/shortcuts/callback_shortcuts.0.dart',
'examples/api/lib/widgets/shortcuts/single_activator.0.dart',
'examples/api/lib/widgets/shortcuts/logical_key_set.0.dart',
'examples/api/lib/widgets/actions/action_listener.0.dart',
'examples/api/lib/widgets/actions/action.action_overridable.0.dart',
'examples/api/lib/widgets/actions/focusable_action_detector.0.dart',
'examples/api/lib/widgets/actions/actions.0.dart',
'examples/api/lib/widgets/widget_state/widget_state_property.0.dart',
'examples/api/lib/widgets/widget_state/widget_state_border_side.0.dart',
'examples/api/lib/widgets/widget_state/widget_state_outlined_border.0.dart',
'examples/api/lib/widgets/widget_state/widget_state_mouse_cursor.0.dart',
'examples/api/lib/widgets/magnifier/magnifier.0.dart',
'examples/api/lib/widgets/navigator/restorable_route_future.0.dart',
'examples/api/lib/widgets/navigator/navigator.restorable_push_and_remove_until.0.dart',
'examples/api/lib/widgets/navigator/navigator_state.restorable_push.0.dart',
'examples/api/lib/widgets/navigator/navigator.restorable_push_replacement.0.dart',
'examples/api/lib/widgets/navigator/navigator_state.restorable_push_and_remove_until.0.dart',
'examples/api/lib/widgets/navigator/navigator_state.restorable_push_replacement.0.dart',
'examples/api/lib/widgets/navigator/navigator.0.dart',
'examples/api/lib/widgets/navigator/navigator.restorable_push.0.dart',
'examples/api/lib/widgets/system_context_menu/system_context_menu.0.dart',
'examples/api/lib/widgets/system_context_menu/system_context_menu.1.dart',
'examples/api/lib/widgets/tween_animation_builder/tween_animation_builder.0.dart',
'examples/api/lib/widgets/page_view/page_view.0.dart',
'examples/api/lib/widgets/page_view/page_view.1.dart',
'examples/api/lib/widgets/hardware_keyboard/key_event_manager.0.dart',
'examples/api/lib/widgets/sliver_fill/sliver_fill_remaining.2.dart',
'examples/api/lib/widgets/sliver_fill/sliver_fill_remaining.3.dart',
'examples/api/lib/widgets/sliver_fill/sliver_fill_remaining.0.dart',
'examples/api/lib/widgets/sliver_fill/sliver_fill_remaining.1.dart',
'examples/api/lib/widgets/interactive_viewer/interactive_viewer.builder.0.dart',
'examples/api/lib/widgets/interactive_viewer/interactive_viewer.0.dart',
'examples/api/lib/widgets/interactive_viewer/interactive_viewer.constrained.0.dart',
'examples/api/lib/widgets/interactive_viewer/interactive_viewer.transformation_controller.0.dart',
'examples/api/lib/widgets/text/ui_testing_with_text.dart',
'examples/api/lib/widgets/text/text.0.dart',
'examples/api/lib/widgets/nested_scroll_view/nested_scroll_view.1.dart',
'examples/api/lib/widgets/nested_scroll_view/nested_scroll_view.0.dart',
'examples/api/lib/widgets/nested_scroll_view/nested_scroll_view.2.dart',
'examples/api/lib/widgets/nested_scroll_view/nested_scroll_view_state.0.dart',
'examples/api/lib/widgets/overscroll_indicator/glowing_overscroll_indicator.1.dart',
'examples/api/lib/widgets/overscroll_indicator/glowing_overscroll_indicator.0.dart',
'examples/api/lib/widgets/transitions/positioned_transition.0.dart',
'examples/api/lib/widgets/transitions/listenable_builder.3.dart',
'examples/api/lib/widgets/transitions/matrix_transition.0.dart',
'examples/api/lib/widgets/transitions/listenable_builder.2.dart',
'examples/api/lib/widgets/transitions/size_transition.0.dart',
'examples/api/lib/widgets/transitions/relative_positioned_transition.0.dart',
'examples/api/lib/widgets/transitions/animated_builder.0.dart',
'examples/api/lib/widgets/transitions/decorated_box_transition.0.dart',
'examples/api/lib/widgets/transitions/rotation_transition.0.dart',
'examples/api/lib/widgets/transitions/fade_transition.0.dart',
'examples/api/lib/widgets/transitions/animated_widget.0.dart',
'examples/api/lib/widgets/transitions/align_transition.0.dart',
'examples/api/lib/widgets/transitions/listenable_builder.1.dart',
'examples/api/lib/widgets/transitions/listenable_builder.0.dart',
'examples/api/lib/widgets/transitions/default_text_style_transition.0.dart',
'examples/api/lib/widgets/transitions/slide_transition.0.dart',
'examples/api/lib/widgets/transitions/scale_transition.0.dart',
'examples/api/lib/widgets/transitions/sliver_fade_transition.0.dart',
'examples/api/lib/widgets/windows/popup.0.dart',
'examples/api/lib/widgets/windows/tooltip.0.dart',
'examples/api/lib/widgets/windows/satellite.0.dart',
'examples/api/lib/widgets/text_editing_intents/editable_text_tap_up_outside_intent.0.dart',
'examples/api/lib/widgets/overlay/overlay_portal.0.dart',
'examples/api/lib/widgets/overlay/overlay.0.dart',
'examples/api/lib/widgets/draggable_scrollable_sheet/draggable_scrollable_sheet.0.dart',
'examples/api/lib/widgets/focus_manager/focus_node.unfocus.0.dart',
'examples/api/lib/widgets/focus_manager/focus_node.0.dart',
'examples/api/lib/widgets/restoration_properties/restorable_value.0.dart',
'examples/api/lib/widgets/gesture_detector/gesture_detector.3.dart',
'examples/api/lib/widgets/gesture_detector/gesture_detector.2.dart',
'examples/api/lib/widgets/gesture_detector/gesture_detector.1.dart',
'examples/api/lib/widgets/gesture_detector/gesture_detector.0.dart',
'examples/api/lib/widgets/routes/show_general_dialog.0.dart',
'examples/api/lib/widgets/routes/local_history_entry.0.dart',
'examples/api/lib/widgets/routes/route_observer.0.dart',
'examples/api/lib/widgets/routes/flexible_route_transitions.1.dart',
'examples/api/lib/widgets/routes/flexible_route_transitions.0.dart',
'examples/api/lib/widgets/routes/popup_route.0.dart',
'examples/api/lib/widgets/repeating_animation_builder/repeating_animation_builder.0.dart',
'examples/api/lib/widgets/scrollbar/raw_scrollbar.1.dart',
'examples/api/lib/widgets/scrollbar/raw_scrollbar.shape.0.dart',
'examples/api/lib/widgets/scrollbar/raw_scrollbar.0.dart',
'examples/api/lib/widgets/scrollbar/raw_scrollbar.2.dart',
'examples/api/lib/widgets/scrollbar/raw_scrollbar.desktop.0.dart',
'examples/api/lib/widgets/inherited_notifier/inherited_notifier.0.dart',
'examples/api/lib/widgets/text_magnifier/text_magnifier.0.dart',
'examples/api/test/widgets/animated_grid/animated_grid.0_test.dart',
'examples/api/test/widgets/animated_grid/sliver_animated_grid.0_test.dart',
'examples/api/test/widgets/navigator_pop_handler/navigator_pop_handler.1_test.dart',
'examples/api/test/widgets/navigator_pop_handler/navigator_pop_handler.0_test.dart',
'examples/api/test/widgets/editable_text/editable_text.on_content_inserted.0_test.dart',
'examples/api/test/widgets/editable_text/text_editing_controller.1_test.dart',
'examples/api/test/widgets/editable_text/editable_text.on_changed.0_test.dart',
'examples/api/test/widgets/editable_text/text_editing_controller.0_test.dart',
'examples/api/test/widgets/undo_history/undo_history_controller.0_test.dart',
'examples/api/test/widgets/raw_tooltip/raw_tooltip.0_test.dart',
'examples/api/test/widgets/form/form.0_test.dart',
'examples/api/test/widgets/form/form.1_test.dart',
'examples/api/test/widgets/tap_region/text_field_tap_region.0_test.dart',
'examples/api/test/widgets/restoration/restoration_mixin.0_test.dart',
'examples/api/test/widgets/drag_target/draggable.0_test.dart',
'examples/api/test/widgets/autocomplete/raw_autocomplete.1_test.dart',
'examples/api/test/widgets/autocomplete/raw_autocomplete.focus_node.0_test.dart',
'examples/api/test/widgets/autocomplete/raw_autocomplete.0_test.dart',
'examples/api/test/widgets/autocomplete/raw_autocomplete.2_test.dart',
'examples/api/test/widgets/keep_alive/automatic_keep_alive.0_test.dart',
'examples/api/test/widgets/keep_alive/keep_alive.0_test.dart',
'examples/api/test/widgets/keep_alive/automatic_keep_alive_client_mixin.0_test.dart',
'examples/api/test/widgets/safe_area/safe_area.0_test.dart',
'examples/api/test/widgets/scroll_notification_observer/scroll_notification_observer.0_test.dart',
'examples/api/test/widgets/animated_size/animated_size.0_test.dart',
'examples/api/test/widgets/framework/build_owner.0_test.dart',
'examples/api/test/widgets/framework/error_widget.0_test.dart',
'examples/api/test/widgets/sliver/pinned_header_sliver.1_test.dart',
'examples/api/test/widgets/sliver/sliver_floating_header.0_test.dart',
'examples/api/test/widgets/sliver/pinned_header_sliver.0_test.dart',
'examples/api/test/widgets/sliver/sliver_opacity.1_test.dart',
'examples/api/test/widgets/sliver/decorated_sliver.0_test.dart',
'examples/api/test/widgets/sliver/sliver_cross_axis_group.0_test.dart',
'examples/api/test/widgets/sliver/decorated_sliver.1_test.dart',
'examples/api/test/widgets/sliver/sliver_main_axis_group.0_test.dart',
'examples/api/test/widgets/sliver/sliver_constrained_cross_axis.0_test.dart',
'examples/api/test/widgets/sliver/sliver_resizing_header.0_test.dart',
'examples/api/test/widgets/heroes/hero.0_test.dart',
'examples/api/test/widgets/heroes/hero.1_test.dart',
'examples/api/test/widgets/dismissible/dismissible.0_test.dart',
'examples/api/test/widgets/overflow_bar/overflow_bar.0_test.dart',
'examples/api/test/widgets/preferred_size/preferred_size.0_test.dart',
'examples/api/test/widgets/media_query/media_query_data.system_gesture_insets.0_test.dart',
'examples/api/test/widgets/focus_traversal/focus_traversal_group.0_test.dart',
'examples/api/test/widgets/focus_traversal/ordered_traversal_policy.0_test.dart',
'examples/api/test/widgets/value_listenable_builder/value_listenable_builder.0_test.dart',
'examples/api/test/widgets/raw_menu_anchor/raw_menu_anchor.3_test.dart',
'examples/api/test/widgets/raw_menu_anchor/raw_menu_anchor.2_test.dart',
'examples/api/test/widgets/raw_menu_anchor/raw_menu_anchor.0_test.dart',
'examples/api/test/widgets/async/stream_builder.0_test.dart',
'examples/api/test/widgets/async/future_builder.0_test.dart',
'examples/api/test/widgets/shared_app_data/shared_app_data.0_test.dart',
'examples/api/test/widgets/shared_app_data/shared_app_data.1_test.dart',
'examples/api/test/widgets/animated_list/animated_list_separated.0_test.dart',
'examples/api/test/widgets/animated_list/sliver_animated_list.0_test.dart',
'examples/api/test/widgets/animated_list/animated_list.0_test.dart',
'examples/api/test/widgets/basic/physical_shape.0_test.dart',
'examples/api/test/widgets/basic/aspect_ratio.2_test.dart',
'examples/api/test/widgets/basic/indexed_stack.0_test.dart',
'examples/api/test/widgets/basic/clip_rrect.1_test.dart',
'examples/api/test/widgets/basic/absorb_pointer.0_test.dart',
'examples/api/test/widgets/basic/listener.0_test.dart',
'examples/api/test/widgets/basic/clip_rrect.0_test.dart',
'examples/api/test/widgets/basic/mouse_region.0_test.dart',
'examples/api/test/widgets/basic/expanded.0_test.dart',
'examples/api/test/widgets/basic/fitted_box.0_test.dart',
'examples/api/test/widgets/basic/mouse_region.on_exit.0_test.dart',
'examples/api/test/widgets/basic/aspect_ratio.0_test.dart',
'examples/api/test/widgets/basic/fractionally_sized_box.0_test.dart',
'examples/api/test/widgets/basic/expanded.1_test.dart',
'examples/api/test/widgets/basic/mouse_region.on_exit.1_test.dart',
'examples/api/test/widgets/basic/custom_multi_child_layout.0_test.dart',
'examples/api/test/widgets/basic/aspect_ratio.1_test.dart',
'examples/api/test/widgets/basic/flow.0_test.dart',
'examples/api/test/widgets/basic/overflowbox.0_test.dart',
'examples/api/test/widgets/scroll_end_notification/scroll_end_notification.0_test.dart',
'examples/api/test/widgets/scroll_end_notification/scroll_end_notification.1_test.dart',
'examples/api/test/widgets/autofill/autofill_group.0_test.dart',
'examples/api/test/widgets/scroll_view/list_view.1_test.dart',
'examples/api/test/widgets/scroll_view/custom_scroll_view.1_test.dart',
'examples/api/test/widgets/scroll_view/list_view.0_test.dart',
'examples/api/test/widgets/image/image.loading_builder.0_test.dart',
'examples/api/test/widgets/image/image.error_builder.0_test.dart',
'examples/api/test/widgets/image/image.frame_builder.0_test.dart',
'examples/api/test/widgets/color_filter/color_filtered.0_test.dart',
'examples/api/test/widgets/implicit_animations/animated_positioned.0_test.dart',
'examples/api/test/widgets/implicit_animations/sliver_animated_opacity.0_test.dart',
'examples/api/test/widgets/implicit_animations/animated_slide.0_test.dart',
'examples/api/test/widgets/implicit_animations/animated_padding.0_test.dart',
'examples/api/test/widgets/implicit_animations/animated_fractionally_sized_box.0_test.dart',
'examples/api/test/widgets/implicit_animations/animated_align.0_test.dart',
'examples/api/test/widgets/implicit_animations/animated_container.0_test.dart',
'examples/api/test/widgets/radio_group/radio_group.0_test.dart',
'examples/api/test/widgets/inherited_theme/inherited_theme.0_test.dart',
'examples/api/test/widgets/scroll_position/scroll_metrics_notification.0_test.dart',
'examples/api/test/widgets/scroll_position/scroll_controller_on_attach.0_test.dart',
'examples/api/test/widgets/scroll_position/is_scrolling_listener.0_test.dart',
'examples/api/test/widgets/scroll_position/scroll_controller_notification.0_test.dart',
'examples/api/test/widgets/page_transitions_builder/page_transitions_builder.0_test.dart',
'examples/api/test/widgets/page_storage/page_storage.0_test.dart',
'examples/api/test/widgets/table/table.0_test.dart',
'examples/api/test/widgets/notification_listener/notification.0_test.dart',
'examples/api/test/widgets/inherited_model/inherited_model.0_test.dart',
'examples/api/test/widgets/focus_scope/focus.0_test.dart',
'examples/api/test/widgets/focus_scope/focus.1_test.dart',
'examples/api/test/widgets/focus_scope/focus.2_test.dart',
'examples/api/test/widgets/focus_scope/focus_scope.0_test.dart',
'examples/api/test/widgets/pop_scope/pop_scope.1_test.dart',
'examples/api/test/widgets/animated_switcher/animated_switcher.0_test.dart',
'examples/api/test/widgets/shortcuts/character_activator.0_test.dart',
'examples/api/test/widgets/actions/action_listener.0_test.dart',
'examples/api/test/widgets/actions/focusable_action_detector.0_test.dart',
'examples/api/test/widgets/actions/actions.0_test.dart',
'examples/api/test/widgets/actions/action.action_overridable.0_test.dart',
'examples/api/test/widgets/widget_state/widget_state_property.0_test.dart',
'examples/api/test/widgets/widget_state/widget_state_border_side.0_test.dart',
'examples/api/test/widgets/widget_state/widget_state_outlined_border.0_test.dart',
'examples/api/test/widgets/widget_state/widget_state_mouse_cursor.0_test.dart',
'examples/api/test/widgets/magnifier/magnifier.0_test.dart',
'examples/api/test/widgets/navigator/navigator.restorable_push.0_test.dart',
'examples/api/test/widgets/navigator/navigator_state.restorable_push_and_remove_until.0_test.dart',
'examples/api/test/widgets/navigator/navigator_state.restorable_push.0_test.dart',
'examples/api/test/widgets/navigator/navigator.restorable_push_and_remove_until.0_test.dart',
'examples/api/test/widgets/navigator/restorable_route_future.0_test.dart',
'examples/api/test/widgets/navigator/navigator_state.restorable_push_replacement.0_test.dart',
'examples/api/test/widgets/navigator/navigator.restorable_push_replacement.0_test.dart',
'examples/api/test/widgets/system_context_menu/system_context_menu.1_test.dart',
'examples/api/test/widgets/system_context_menu/system_context_menu.0_test.dart',
'examples/api/test/widgets/tween_animation_builder/tween_animation_builder.0_test.dart',
'examples/api/test/widgets/page_view/page_view.0_test.dart',
'examples/api/test/widgets/page_view/page_view.1_test.dart',
'examples/api/test/widgets/hardware_keyboard/key_event_manager.0_test.dart',
'examples/api/test/widgets/sliver_fill/sliver_fill_remaining.1_test.dart',
'examples/api/test/widgets/sliver_fill/sliver_fill_remaining.0_test.dart',
'examples/api/test/widgets/sliver_fill/sliver_fill_remaining.3_test.dart',
'examples/api/test/widgets/sliver_fill/sliver_fill_remaining.2_test.dart',
'examples/api/test/widgets/interactive_viewer/interactive_viewer.transformation_controller.0_test.dart',
'examples/api/test/widgets/interactive_viewer/interactive_viewer.0_test.dart',
'examples/api/test/widgets/interactive_viewer/interactive_viewer.constrained.0_test.dart',
'examples/api/test/widgets/text/text.0_test.dart',
'examples/api/test/widgets/nested_scroll_view/nested_scroll_view.2_test.dart',
'examples/api/test/widgets/nested_scroll_view/nested_scroll_view_state.0_test.dart',
'examples/api/test/widgets/nested_scroll_view/nested_scroll_view.0_test.dart',
'examples/api/test/widgets/nested_scroll_view/nested_scroll_view.1_test.dart',
'examples/api/test/widgets/overscroll_indicator/glowing_overscroll_indicator.0_test.dart',
'examples/api/test/widgets/overscroll_indicator/glowing_overscroll_indicator.1_test.dart',
'examples/api/test/widgets/transitions/listenable_builder.3_test.dart',
'examples/api/test/widgets/transitions/sliver_fade_transition.0_test.dart',
'examples/api/test/widgets/transitions/matrix_transition.0_test.dart',
'examples/api/test/widgets/transitions/default_text_style_transition.0_test.dart',
'examples/api/test/widgets/transitions/listenable_builder.2_test.dart',
'examples/api/test/widgets/transitions/align_transition.0_test.dart',
'examples/api/test/widgets/transitions/size_transition.0_test.dart',
'examples/api/test/widgets/transitions/fade_transition.0_test.dart',
'examples/api/test/widgets/transitions/listenable_builder.1_test.dart',
'examples/api/test/widgets/transitions/relative_positioned_transition.0_test.dart',
'examples/api/test/widgets/transitions/slide_transition.0_test.dart',
'examples/api/test/widgets/transitions/positioned_transition.0_test.dart',
'examples/api/test/widgets/transitions/decorated_box_transition.0_test.dart',
'examples/api/test/widgets/transitions/animated_builder.0_test.dart',
'examples/api/test/widgets/transitions/listenable_builder.0_test.dart',
'examples/api/test/widgets/transitions/scale_transition.0_test.dart',
'examples/api/test/widgets/transitions/animated_widget.0_test.dart',
'examples/api/test/widgets/transitions/rotation_transition.0_test.dart',
'examples/api/test/widgets/text_editing_intents/editable_text_tap_up_outside_intent.0_test.dart',
'examples/api/test/widgets/overlay/overlay.0_test.dart',
'examples/api/test/widgets/draggable_scrollable_sheet/draggable_scrollable_sheet.0_test.dart',
'examples/api/test/widgets/focus_manager/focus_node.unfocus.0_test.dart',
'examples/api/test/widgets/focus_manager/focus_node.0_test.dart',
'examples/api/test/widgets/restoration_properties/restorable_value.0_test.dart',
'examples/api/test/widgets/gesture_detector/gesture_detector.3_test.dart',
'examples/api/test/widgets/gesture_detector/gesture_detector.2_test.dart',
'examples/api/test/widgets/gesture_detector/gesture_detector.1_test.dart',
'examples/api/test/widgets/gesture_detector/gesture_detector.0_test.dart',
'examples/api/test/widgets/routes/popup_route.0_test.dart',
'examples/api/test/widgets/routes/show_general_dialog.0_test.dart',
'examples/api/test/widgets/repeating_animation_builder/repeating_animation_builder.0_test.dart',
'examples/api/test/widgets/scrollbar/raw_scrollbar.2_test.dart',
'examples/api/test/widgets/scrollbar/raw_scrollbar.desktop.0_test.dart',
'examples/api/test/widgets/scrollbar/raw_scrollbar.0_test.dart',
'examples/api/test/widgets/scrollbar/raw_scrollbar.shape.0_test.dart',
'examples/api/test/widgets/scrollbar/raw_scrollbar.1_test.dart',
'examples/api/test/widgets/inherited_notifier/inherited_notifier.0_test.dart',
'examples/api/test/widgets/text_magnifier/text_magnifier.0_test.dart',
'examples/flutter_view/lib/main.dart',
'examples/image_list/lib/main.dart',
'examples/layers/rendering/touch_input.dart',
'examples/layers/services/isolate.dart',
'examples/layers/widgets/gestures.dart',
'examples/layers/widgets/spinning_mixed.dart',
'examples/layers/widgets/media_query.dart',
'examples/layers/widgets/sectors.dart',
'examples/layers/widgets/styled_text.dart',
'examples/layers/test/gestures_test.dart',
'examples/multiple_windows/lib/app/main_window.dart',
'examples/multiple_windows/lib/app/tooltip_button.dart',
'examples/multiple_windows/lib/app/tooltip_window_edit_dialog.dart',
'examples/multiple_windows/lib/app/dialog_window_content.dart',
'examples/multiple_windows/lib/app/dialog_window_edit_dialog.dart',
'examples/multiple_windows/lib/app/popup_window_content.dart',
'examples/multiple_windows/lib/app/window_content.dart',
'examples/multiple_windows/lib/app/window_edit_dialog.dart',
'examples/multiple_windows/lib/app/rotated_wire_cube.dart',
'examples/multiple_windows/lib/app/popup_window_edit_dialog.dart',
'examples/multiple_windows/lib/app/tooltip_window_content.dart',
'examples/multiple_windows/lib/app/popup_button.dart',
'examples/multiple_windows/lib/app/window_settings_dialog.dart',
'examples/multiple_windows/lib/main.dart',
'examples/multiple_windows/test/multiple_windows_test.dart',
'examples/platform_channel/lib/main.dart',
'examples/platform_channel_swift/lib/main.dart',
'examples/platform_view/lib/main.dart',
'examples/splash/lib/main.dart',
'examples/splash/test/splash_test.dart',
'examples/texture/lib/main.dart',
};
static final RegExp _examplesPrefix = RegExp(r'examples');
/// Find the `examples/api/lib` and `examples/api/test` directories
/// which contain the API examples and relevant tests.
///
/// For the cross imports checker, only the `examples/api/lib` and `examples/api/test` directories are relevant.
/// The other directories in `examples/api` are either generated (e.g. build or .dart_tool),
/// platform directories for the samples (e.g. windows or linux),
/// or a shim for the integration test driver.
({Directory libDirectory, Directory testDirectory}) _findExamplesSlashApiDirectories(
Directory examplesSlashApiDirectory,
) {
Directory? examplesSlashApiLibDirectory;
Directory? examplesSlashApiTestDirectory;
for (final Directory directory in examplesSlashApiDirectory.listSync().whereType<Directory>()) {
final String directoryName = path.basename(directory.absolute.path);
if (directoryName == 'lib' && examplesSlashApiLibDirectory == null) {
examplesSlashApiLibDirectory = directory;
} else if (directoryName == 'test' && examplesSlashApiTestDirectory == null) {
examplesSlashApiTestDirectory = directory;
}
}
if (examplesSlashApiLibDirectory == null) {
throw StateError('Could not find lib directory in examples/api.');
}
if (examplesSlashApiTestDirectory == null) {
throw StateError('Could not find test directory in examples/api.');
}
return (
libDirectory: examplesSlashApiLibDirectory,
testDirectory: examplesSlashApiTestDirectory,
);
}
/// Get a list of all the filenames that end in ".dart", grouped by library.
Map<_ExamplesLibrary, Set<File>> _getExampleFiles() {
final dartFilePattern = RegExp(r'\.dart$');
const _ExamplesLibrary examplesRoot = _GenericExampleLibrary('examples');
final Map<_ExamplesLibrary, Set<File>> mapping = {examplesRoot: {}};
// List the files directly under `examples` and then walk the subdirectories.
for (final FileSystemEntity fileSystemEntity in examplesDirectory.listSync()) {
if (fileSystemEntity is File && fileSystemEntity.absolute.path.contains(dartFilePattern)) {
mapping[examplesRoot]?.add(fileSystemEntity);
continue;
}
if (fileSystemEntity is! Directory) {
continue;
}
final String directoryName = path.basename(fileSystemEntity.absolute.path);
if (directoryName == 'build' || directoryName == '.dart_tool') {
continue;
}
// The examples/api folder contains examples in a single Flutter project,
// grouped in subfolders in lib/ and test/, so these need to be handled separately.
if (directoryName == 'api') {
final examplesSlashApiLibrary = _ExamplesLibrary.fromDirectory(
fileSystemEntity,
flutterRoot: flutterRoot,
);
// First list the files directly under examples/api.
mapping[examplesSlashApiLibrary] = {
for (final File file in fileSystemEntity.listSync().whereType<File>())
if (file.absolute.path.contains(dartFilePattern)) file,
};
final (:Directory libDirectory, :Directory testDirectory) =
_findExamplesSlashApiDirectories(fileSystemEntity);
// Handle the files under examples/api/lib/sample_templates and examples/api/test/sample_templates,
// which list individual files with a specific file pattern.
mapping.addAll(
_getExamplesSlashApiSampleTemplatesFiles(
libDirectory: libDirectory,
testDirectory: testDirectory,
dartFilePattern: dartFilePattern,
),
);
// Handle the other samples, which are divided per subfolder.
mapping.addAll(
_getExamplesSlashApiExamples(
libDirectory: libDirectory,
testDirectory: testDirectory,
dartFilePattern: dartFilePattern,
),
);
continue;
}
final library = _ExamplesLibrary.fromDirectory(fileSystemEntity, flutterRoot: flutterRoot);
mapping[library] = _getExampleFilesForDirectory(
fileSystemEntity,
dartFilePattern: dartFilePattern,
);
}
return mapping;
}
/// Get a list of all the filenames that end in ".dart" for the given examples directory.
///
/// The [directory] must not be a subdirectory of `examples/api`.
Set<File> _getExampleFilesForDirectory(Directory directory, {required Pattern dartFilePattern}) {
final String examplesSlashApiPath = path.join(flutterRoot.absolute.path, 'examples', 'api');
if (directory.absolute.path.startsWith(examplesSlashApiPath)) {
throw ArgumentError('Directory must not be an examples/api subdirectory.', 'directory');
}
final files = <File>{};
final queue = <Directory>[directory];
while (queue.isNotEmpty) {
final Directory current = queue.removeAt(0);
for (final FileSystemEntity fileSystemEntity in current.listSync()) {
if (fileSystemEntity is File && fileSystemEntity.absolute.path.contains(dartFilePattern)) {
files.add(fileSystemEntity);
continue;
}
if (fileSystemEntity is! Directory) {
continue;
}
final String directoryName = path.basename(fileSystemEntity.absolute.path);
if (directoryName == 'build' || directoryName == '.dart_tool') {
continue;
}
queue.add(fileSystemEntity);
}
}
return files;
}
/// Get a list of all the filenames that end in ".dart", grouped by library,
/// for the subdrectories of `examples/api/lib/sample_templates` and `examples/api/test/sample_templates`.
Map<_SampleTemplatesLibraryFile, Set<File>> _getExamplesSlashApiSampleTemplatesFiles({
required Directory libDirectory,
required Directory testDirectory,
required Pattern dartFilePattern,
}) {
final Directory sampleTemplatesLibDirectory = libDirectory.childDirectory(
_kSampleTemplatesDirectoryName,
);
final Directory sampleTemplatesTestDirectory = testDirectory.childDirectory(
_kSampleTemplatesDirectoryName,
);
final Map<_SampleTemplatesLibraryFile, Set<File>> mapping = {};
for (final File file
in sampleTemplatesLibDirectory.listSync(recursive: true).whereType<File>()) {
if (file.absolute.path.contains(dartFilePattern)) {
mapping[_SampleTemplatesLibraryFile.fromFile(file)] = {file};
}
}
for (final File file
in sampleTemplatesTestDirectory.listSync(recursive: true).whereType<File>()) {
if (file.absolute.path.contains(dartFilePattern)) {
mapping[_SampleTemplatesLibraryFile.fromFile(file)] = {file};
}
}
return mapping;
}
/// Get a list of all the filenames that end in ".dart", grouped by library,
/// for the subdirectories of `examples/api`,
/// except `examples/api/lib/sample_templates` and `examples/api/test/sample_templates`.
Map<_ExamplesLibrary, Set<File>> _getExamplesSlashApiExamples({
required Directory libDirectory,
required Directory testDirectory,
required Pattern dartFilePattern,
}) {
final Map<_ExamplesLibrary, Set<File>> mapping = {};
for (final Directory directory in libDirectory.listSync().whereType<Directory>()) {
// The sample templates directory is handled separately.
if (path.basename(directory.absolute.path) == _kSampleTemplatesDirectoryName) {
continue;
}
final library = _ExamplesLibrary.fromDirectory(directory, flutterRoot: flutterRoot);
mapping.putIfAbsent(library, () => {});
for (final File file in directory.listSync(recursive: true).whereType<File>()) {
if (!file.absolute.path.contains(dartFilePattern)) {
continue;
}
mapping[library]?.add(file);
}
}
for (final Directory directory in testDirectory.listSync().whereType<Directory>()) {
// The sample templates directory is handled separately.
if (path.basename(directory.absolute.path) == _kSampleTemplatesDirectoryName) {
continue;
}
final library = _ExamplesLibrary.fromDirectory(directory, flutterRoot: flutterRoot);
mapping.putIfAbsent(library, () => {});
for (final File file in directory.listSync(recursive: true).whereType<File>()) {
if (!file.absolute.path.contains(dartFilePattern)) {
continue;
}
mapping[library]?.add(file);
}
}
return mapping;
}
/// Returns true if there are no errors, false otherwise.
bool check() {
filesystem.currentDirectory = flutterRoot;
final Map<_ExamplesLibrary, Set<File>> filesByLibrary = _getExampleFiles();
// Find all cross imports.
final Map<CrossImportCheckedLibrary, CrossImportingFiles> crossImportsPerLibrary =
getCrossImports(filesByLibrary);
var valid = true;
// Find any cross imports that are not in the known list.
for (final MapEntry<CrossImportCheckedLibrary, CrossImportingFiles> entry
in crossImportsPerLibrary.entries) {
final Set<File> unknownCupertinoImports = getUnknowns(
knownExamplesCrossImports,
entry.value.cupertinoImports,
prefix: _examplesPrefix,
);
final Set<File> unknownMaterialImports = getUnknowns(
knownExamplesCrossImports,
entry.value.materialImports,
prefix: _examplesPrefix,
);
if (unknownMaterialImports.isNotEmpty) {
valid = false;
foundError(
getImportError(
flutterRoot: flutterRoot,
files: unknownMaterialImports,
checkedLibrary: entry.key,
importStatement: LibraryCrossImportStatementType.material,
).split('\n'),
);
}
if (unknownCupertinoImports.isNotEmpty) {
valid = false;
foundError(
getImportError(
flutterRoot: flutterRoot,
files: unknownCupertinoImports,
checkedLibrary: entry.key,
importStatement: LibraryCrossImportStatementType.cupertino,
).split('\n'),
);
}
}
// Find any known cross imports that weren't found, and are therefore fixed.
// Pre-compute all library prefixes so that root libraries (e.g. `examples`,
// `examples/api`) don't claim entries that belong to a more-specific sub-library.
// TODO(justinmc): Remove this after all known cross imports have been
// fixed.
// See https://github.com/flutter/flutter/issues/187645.
final Set<String> allLibraryPrefixes = {
for (final CrossImportCheckedLibrary library in crossImportsPerLibrary.keys)
'${library.libraryName}/',
};
for (final MapEntry<CrossImportCheckedLibrary, CrossImportingFiles> entry
in crossImportsPerLibrary.entries) {
final ownPrefix = '${entry.key.libraryName}/';
final Set<File> crossImportsForLibrary = entry.value.cupertinoImports.union(
entry.value.materialImports,
);
final Set<String> knownCrossImportsForLibrary = {
for (final String element in entry.key.knownCrossImports)
// The known cross imports include both /lib and /test entries, so handle both.
// Exclude entries that are owned by a more-specific sub-library.
if (element.startsWith(ownPrefix) &&
!allLibraryPrefixes.any(
(String prefix) =>
prefix != ownPrefix &&
prefix.startsWith(ownPrefix) &&
element.startsWith(prefix),
))
element,
};
final Set<String> fixedCrossImports = differencePaths(
knownCrossImportsForLibrary,
crossImportsForLibrary,
prefix: _examplesPrefix,
);
if (fixedCrossImports.isNotEmpty) {
valid = false;
foundError(getFixedImportError(fixedCrossImports, entry.key).split('\n'));
}
}
return valid;
}
}
/// The examples that we are concerned with cross importing.
sealed class _ExamplesLibrary implements CrossImportCheckedLibrary {
const _ExamplesLibrary(this._name);
/// Construct a [_ExamplesLibrary] from a given [directory].
///
/// The [directory] must be inside the [flutterRoot].
factory _ExamplesLibrary.fromDirectory(Directory directory, {required Directory flutterRoot}) {
if (!directory.absolute.path.startsWith(flutterRoot.absolute.path)) {
throw ArgumentError('Directory must be inside ${flutterRoot.absolute.path}.', 'directory');
}
final String relativePath = path
.relative(directory.absolute.path, from: flutterRoot.absolute.path)
.replaceAll(Platform.pathSeparator, '/');
return switch (relativePath) {
_
when relativePath.startsWith('examples/api/lib/cupertino') ||
relativePath.startsWith('examples/api/test/cupertino') =>
_CupertinoApiExampleLibrary(relativePath),
_
when relativePath.startsWith('examples/api/lib/material') ||
relativePath.startsWith('examples/api/test/material') =>
_MaterialApiExampleLibrary(relativePath),
_
when relativePath.startsWith('examples/api/lib/animation') ||
relativePath.startsWith('examples/api/lib/foundation') ||
relativePath.startsWith('examples/api/lib/gestures') ||
relativePath.startsWith('examples/api/lib/painting') ||
relativePath.startsWith('examples/api/lib/rendering') ||
relativePath.startsWith('examples/api/lib/services') ||
relativePath.startsWith('examples/api/lib/ui') ||
relativePath.startsWith('examples/api/lib/widgets') =>
_ApiExampleLibrary(relativePath),
_
when relativePath.startsWith('examples/api/test/animation') ||
relativePath.startsWith('examples/api/test/foundation') ||
relativePath.startsWith('examples/api/test/gestures') ||
relativePath.startsWith('examples/api/test/painting') ||
relativePath.startsWith('examples/api/test/rendering') ||
relativePath.startsWith('examples/api/test/services') ||
relativePath.startsWith('examples/api/test/ui') ||
relativePath.startsWith('examples/api/test/widgets') =>
_ApiExampleLibrary(relativePath),
_
when relativePath.startsWith('examples/flutter_view') ||
relativePath.startsWith('examples/hello_world') ||
relativePath.startsWith('examples/image_list') ||
relativePath.startsWith('examples/layers') ||
relativePath.startsWith('examples/multiple_windows') ||
relativePath.startsWith('examples/platform_channel') ||
relativePath.startsWith('examples/platform_channel_swift') ||
relativePath.startsWith('examples/platform_view') ||
relativePath.startsWith('examples/splash') ||
relativePath.startsWith('examples/texture') =>
_ApiExampleLibrary(relativePath),
_ when relativePath.startsWith('examples/api') || relativePath.startsWith('examples') =>
_ApiExampleLibrary(relativePath),
_ => throw UnimplementedError('Unknown library: $relativePath'),
};
}
/// The short name of the library, for example `examples/flutter_view`.
final String _name;
@override
String get cannotImportMessage {
return 'Only Material examples can import Material and only Cupertino examples can import Cupertino.';
}
@override
Set<String> get knownCrossImports => ExamplesCrossImportChecker.knownExamplesCrossImports;
@override
String get libraryName => _name;
@override
String get removeCrossImportsInstructionMessage {
return 'However, they now need to be removed from the\n'
'knownExamplesCrossImports list in the script /dev/bots/check_examples_cross_imports.dart.';
}
@override
bool canImport(LibraryCrossImportStatementType import) => false;
@override
String getDisallowedImportMessage(String importedLibraryName, int filesCount) {
return filesCount < 2
? 'The following file in $libraryName has a disallowed import of $importedLibraryName. '
'Refactor it or move it to the $importedLibraryName examples.\n'
: 'The following $filesCount files in $libraryName have a disallowed import of $importedLibraryName. '
'Refactor them or move them to the $importedLibraryName examples.\n';
}
}
/// Any API example - not related to Material or Cupertino - inside `examples/api`, and its tests.
///
/// For example `examples/api/lib/foundation` and `examples/api/test/foundation`.
final class _ApiExampleLibrary extends _ExamplesLibrary {
const _ApiExampleLibrary(super.name);
}
/// The examples in `examples/api/lib/cupertino`
/// and their tests in `examples/api/test/cupertino`.
final class _CupertinoApiExampleLibrary extends _ExamplesLibrary {
const _CupertinoApiExampleLibrary(super.name);
@override
bool canImport(LibraryCrossImportStatementType import) {
// While the Cupertino examples under `examples/api` are not allowed to import Material,
// the actual samples have been relocated to `packages/cupertino_ui`,
// where their cross imports will be addressed separately.
// The existing samples in `examples/api` are the now defunct orginal samples.
// For the purpose of the checker, allow all imports.
return true;
}
}
/// Any non-API example, not in `examples/api`,
/// such as `examples/flutter_view` or `examples/hello_world`.
final class _GenericExampleLibrary extends _ExamplesLibrary {
const _GenericExampleLibrary(super.name);
}
/// The examples in `examples/api/lib/material`
/// and their tests in `examples/api/test/material`.
final class _MaterialApiExampleLibrary extends _ExamplesLibrary {
const _MaterialApiExampleLibrary(super.name);
@override
bool canImport(LibraryCrossImportStatementType import) {
// While the Material examples under `examples/api` are not allowed to import Cupertino,
// the actual samples have been relocated to `packages/material_ui`,
// where their cross imports will be addressed separately.
// The existing samples in `examples/api` are the now defunct orginal samples.
// For the purpose of the checker, allow all imports.
return true;
}
}
/// The examples in `examples/api/lib/sample_templates`
/// and their tests in `examples/api/test/sample_templates`.
///
/// The sample templates are individual files, rather than directories.
final class _SampleTemplatesLibraryFile extends _ExamplesLibrary {
const _SampleTemplatesLibraryFile._(super.name, this._filePath);
factory _SampleTemplatesLibraryFile.fromFile(File file) {
const examplesLibPrefix = 'examples/api/lib/sample_templates';
const examplesTestPrefix = 'examples/api/test/sample_templates';
final String filePath = file.absolute.path.replaceAll(Platform.pathSeparator, '/');
final int libIndex = filePath.indexOf(examplesLibPrefix);
if (libIndex != -1) {
return _SampleTemplatesLibraryFile._(examplesLibPrefix, filePath);
}
final int testIndex = filePath.indexOf(examplesTestPrefix);
if (testIndex != -1) {
return _SampleTemplatesLibraryFile._(examplesTestPrefix, filePath);
}
throw ArgumentError('Invalid file path: $filePath');
}
/// The file path to the template file.
final String _filePath;
@override
bool canImport(LibraryCrossImportStatementType import) {
return switch (import) {
LibraryCrossImportStatementType.material => _filePath.contains('material'),
LibraryCrossImportStatementType.cupertino => _filePath.contains('cupertino'),
};
}
}