// 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. import 'package:flutter/material.dart'; /// Examples for [LinearBorder] and [LinearBorderEdge]. void main() { runApp(const ExampleApp()); } class ExampleApp extends StatelessWidget { const ExampleApp({super.key}); @override Widget build(BuildContext context) { return const MaterialApp( home: Directionality( // TRY THIS: Switch to TextDirection.rtl to see how the borders change. textDirection: TextDirection.ltr, child: Home(), ), ); } } class SampleCard extends StatelessWidget { const SampleCard({ super.key, required this.title, required this.subtitle, required this.children, }); final String title; final String subtitle; final List children; @override Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); final TextTheme textTheme = theme.textTheme; final ColorScheme colorScheme = theme.colorScheme; return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text(title, style: textTheme.titleMedium), Text( subtitle, style: textTheme.bodyMedium!.copyWith( color: colorScheme.secondary, ), ), const SizedBox(height: 16), Row( children: List.generate(children.length * 2 - 1, ( int index, ) { return index.isEven ? children[index ~/ 2] : const SizedBox(width: 16); }), ), ], ), ), ); } } class Home extends StatefulWidget { const Home({super.key}); @override State createState() => _HomeState(); } class _HomeState extends State { final LinearBorder shape0 = LinearBorder.top(); final LinearBorder shape1 = LinearBorder.top(size: 0); late LinearBorder shape = shape0; @override Widget build(BuildContext context) { final ColorScheme colorScheme = Theme.of(context).colorScheme; final BorderSide primarySide0 = BorderSide( width: 0, color: colorScheme.inversePrimary, ); // hairline final BorderSide primarySide2 = BorderSide( width: 2, color: colorScheme.onPrimaryContainer, ); final BorderSide primarySide3 = BorderSide( width: 3, color: colorScheme.inversePrimary, ); return Scaffold( body: SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(16), child: Column( mainAxisSize: MainAxisSize.min, children: [ // Demonstrates using LinearBorder.bottom() to define // an underline border for the standard button types. // The underline's color and width is defined by the ButtonStyle's // side parameter. The side can also be specified as a // LinearBorder parameter and if both are specified then the // ButtonStyle's side is used. This set up makes it possible // for a button theme to specify the shape and for individual // buttons to specify the shape border's color and width. SampleCard( title: 'LinearBorder.bottom()', subtitle: 'Standard button widgets', children: [ TextButton( style: TextButton.styleFrom( side: primarySide3, shape: LinearBorder.bottom(), ), onPressed: () {}, child: const Text('Text'), ), OutlinedButton( style: OutlinedButton.styleFrom( side: primarySide3, shape: LinearBorder.bottom(), ), onPressed: () {}, child: const Text('Outlined'), ), ElevatedButton( style: ElevatedButton.styleFrom( side: primarySide3, shape: LinearBorder.bottom(), ), onPressed: () {}, child: const Text('Elevated'), ), ], ), const SizedBox(height: 32), // Demonstrates creating LinearBorders with a single edge // by using the convenience constructors like LinearBorder.start(). // The edges are drawn with a BorderSide with width:0, which // means that a "hairline" line is stroked. Wider borders are // drawn with filled rectangles. SampleCard( title: 'LinearBorder', subtitle: 'Convenience constructors', children: [ TextButton( style: TextButton.styleFrom( side: primarySide0, shape: LinearBorder.start(), ), onPressed: () {}, child: const Text('Start()'), ), TextButton( style: TextButton.styleFrom( side: primarySide0, shape: LinearBorder.end(), ), onPressed: () {}, child: const Text('End()'), ), TextButton( style: TextButton.styleFrom( side: primarySide0, shape: LinearBorder.top(), ), onPressed: () {}, child: const Text('Top()'), ), TextButton( style: TextButton.styleFrom( side: primarySide0, shape: LinearBorder.bottom(), ), onPressed: () {}, child: const Text('Bottom()'), ), ], ), const SizedBox(height: 32), // Demonstrates creating LinearBorders with a single edge // that's smaller than the button's bounding box. The size // parameter specifies a percentage of the available space // and alignment is -1 for start-alignment, 0 for centered, // and 1 for end-alignment. SampleCard( title: 'LinearBorder', subtitle: 'Size and alignment parameters', children: [ TextButton( style: TextButton.styleFrom( side: primarySide2, shape: LinearBorder.bottom(size: 0.5), ), onPressed: () {}, child: const Text('Center'), ), TextButton( style: TextButton.styleFrom( side: primarySide2, shape: LinearBorder.bottom(size: 0.75, alignment: -1), ), onPressed: () {}, child: const Text('Start'), ), TextButton( style: TextButton.styleFrom( side: primarySide2, shape: LinearBorder.bottom(size: 0.75, alignment: 1), ), onPressed: () {}, child: const Text('End'), ), ], ), const SizedBox(height: 32), // Demonstrates creating LinearBorders with more than one edge. // In these cases the default constructor is used and each edge // is defined with one LinearBorderEdge object. SampleCard( title: 'LinearBorder', subtitle: 'LinearBorderEdge parameters', children: [ TextButton( style: TextButton.styleFrom( side: primarySide0, shape: const LinearBorder( top: LinearBorderEdge(), bottom: LinearBorderEdge(), ), ), onPressed: () {}, child: const Text('Horizontal'), ), TextButton( style: TextButton.styleFrom( side: primarySide0, shape: const LinearBorder( start: LinearBorderEdge(), end: LinearBorderEdge(), ), ), onPressed: () {}, child: const Text('Vertical'), ), TextButton( style: TextButton.styleFrom( side: primarySide0, shape: const LinearBorder( start: LinearBorderEdge(), bottom: LinearBorderEdge(), ), ), onPressed: () {}, child: const Text('Corner'), ), ], ), const SizedBox(height: 32), // Demonstrates that changing properties of LinearBorders // causes them to animate to their new configuration. SampleCard( title: 'Interpolation', subtitle: 'LinearBorder.top() => LinearBorder.top(size: 0)', children: [ IconButton( icon: const Icon(Icons.play_arrow), onPressed: () { setState(() { shape = shape == shape0 ? shape1 : shape0; }); }, ), TextButton( style: TextButton.styleFrom( side: primarySide3, shape: shape, ), onPressed: () {}, child: const Text('Press Play'), ), TextButton( style: ButtonStyle( side: WidgetStateProperty.resolveWith(( Set states, ) { return states.contains(WidgetState.hovered) ? primarySide3 : null; }), shape: WidgetStateProperty.resolveWith(( Set states, ) { return states.contains(WidgetState.hovered) ? shape0 : shape1; }), ), onPressed: () {}, child: const Text('Hover'), ), ], ), ], ), ), ), ); } }