// Copyright 2015 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 'basic.dart'; import 'container.dart'; import 'debug.dart'; import 'framework.dart'; import 'gesture_detector.dart'; import 'navigator.dart'; import 'transitions.dart'; /// A widget that prevents the user from interacting with widgets behind itself. /// /// The modal barrier is the scrim that is rendered behind each route, which /// generally prevents the user from interacting with the route below the /// current route, and normally partially obscures such routes. /// /// For example, when a dialog is on the screen, the page below the dialog is /// usually darkened by the modal barrier. /// /// See also: /// /// * [ModalRoute], which indirectly uses this widget. /// * [AnimatedModalBarrier], which is similar but takes an animated [color] /// instead of a single color value. class ModalBarrier extends StatelessWidget { /// Creates a widget that blocks user interaction. const ModalBarrier({ Key key, this.color, this.dismissible = true, this.semanticsLabel, this.barrierSemanticsDismissible = true, }) : super(key: key); /// If non-null, fill the barrier with this color. /// /// See also: /// /// * [ModalRoute.barrierColor], which controls this property for the /// [ModalBarrier] built by [ModalRoute] pages. final Color color; /// Whether touching the barrier will pop the current route off the [Navigator]. /// /// See also: /// /// * [ModalRoute.barrierDismissible], which controls this property for the /// [ModalBarrier] built by [ModalRoute] pages. final bool dismissible; /// Whether the modal barrier semantics are included in the semantics tree. /// /// See also: /// /// * [ModalRoute.semanticsDismissible], which controls this property for /// the [ModalBarrier] built by [ModalRoute] pages. final bool barrierSemanticsDismissible; /// Semantics label used for the barrier if it is [dismissible]. /// /// The semantics label is read out by accessibility tools (e.g. TalkBack /// on Android and VoiceOver on iOS) when the barrier is focused. /// /// See also: /// /// * [ModalRoute.barrierLabel], which controls this property for the /// [ModalBarrier] built by [ModalRoute] pages. final String semanticsLabel; @override Widget build(BuildContext context) { assert(!dismissible || semanticsLabel == null || debugCheckHasDirectionality(context)); final bool semanticsDismissible = dismissible && defaultTargetPlatform != TargetPlatform.android; final bool modalBarrierSemanticsDismissible = barrierSemanticsDismissible ?? semanticsDismissible; return BlockSemantics( child: ExcludeSemantics( // On Android, the back button is used to dismiss a modal. On iOS, some // modal barriers are not dismissible in accessibility mode. excluding: !semanticsDismissible || !modalBarrierSemanticsDismissible, child: GestureDetector( onTapDown: (TapDownDetails details) { if (dismissible) Navigator.maybePop(context); }, behavior: HitTestBehavior.opaque, child: Semantics( label: semanticsDismissible ? semanticsLabel : null, textDirection: semanticsDismissible && semanticsLabel != null ? Directionality.of(context) : null, child: ConstrainedBox( constraints: const BoxConstraints.expand(), child: color == null ? null : DecoratedBox( decoration: BoxDecoration( color: color, ), ), ), ), ), ), ); } } /// A widget that prevents the user from interacting with widgets behind itself, /// and can be configured with an animated color value. /// /// The modal barrier is the scrim that is rendered behind each route, which /// generally prevents the user from interacting with the route below the /// current route, and normally partially obscures such routes. /// /// For example, when a dialog is on the screen, the page below the dialog is /// usually darkened by the modal barrier. /// /// This widget is similar to [ModalBarrier] except that it takes an animated /// [color] instead of a single color. /// /// See also: /// /// * [ModalRoute], which uses this widget. class AnimatedModalBarrier extends AnimatedWidget { /// Creates a widget that blocks user interaction. const AnimatedModalBarrier({ Key key, Animation<Color> color, this.dismissible = true, this.semanticsLabel, this.barrierSemanticsDismissible, }) : super(key: key, listenable: color); /// If non-null, fill the barrier with this color. /// /// See also: /// /// * [ModalRoute.barrierColor], which controls this property for the /// [AnimatedModalBarrier] built by [ModalRoute] pages. Animation<Color> get color => listenable; /// Whether touching the barrier will pop the current route off the [Navigator]. /// /// See also: /// /// * [ModalRoute.barrierDismissible], which controls this property for the /// [AnimatedModalBarrier] built by [ModalRoute] pages. final bool dismissible; /// Semantics label used for the barrier if it is [dismissible]. /// /// The semantics label is read out by accessibility tools (e.g. TalkBack /// on Android and VoiceOver on iOS) when the barrier is focused. /// See also: /// /// * [ModalRoute.barrierLabel], which controls this property for the /// [ModalBarrier] built by [ModalRoute] pages. final String semanticsLabel; /// Whether the modal barrier semantics are included in the semantics tree. /// /// See also: /// /// * [ModalRoute.semanticsDismissible], which controls this property for /// the [ModalBarrier] built by [ModalRoute] pages. final bool barrierSemanticsDismissible; @override Widget build(BuildContext context) { return ModalBarrier( color: color?.value, dismissible: dismissible, semanticsLabel: semanticsLabel, barrierSemanticsDismissible: barrierSemanticsDismissible, ); } }