// 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'; /// Flutter code sample for [AppBar.shape]. void main() => runApp(const AppBarExampleApp()); class AppBarExampleApp extends StatelessWidget { const AppBarExampleApp({super.key}); @override Widget build(BuildContext context) { return const MaterialApp( home: AppBarExample(), ); } } class AppBarExample extends StatelessWidget { const AppBarExample({super.key}); @override Widget build(BuildContext context) { final ColorScheme colorScheme = Theme.of(context).colorScheme; return Scaffold( appBar: AppBar( shape: const CustomAppBarShape(), backgroundColor: colorScheme.primaryContainer, title: const Text('AppBar Sample'), bottom: PreferredSize( preferredSize: const Size.fromHeight(64.0), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: TextField( decoration: InputDecoration( border: OutlineInputBorder( borderSide: BorderSide(color: colorScheme.primary), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide(color: colorScheme.onPrimaryContainer), ), filled: true, hintText: 'Enter a search term', fillColor: colorScheme.surface, prefixIcon: Icon( Icons.search_rounded, color: colorScheme.primary ), suffixIcon: Icon( Icons.tune_rounded, color: colorScheme.primary ), ) ), ), ), ), body: ListView.builder( padding: const EdgeInsets.only(top: 45.0), itemCount: 20, itemBuilder: (BuildContext context, int index) { return ListTile( title: Text('Item $index'), ); }, ), ); } } class CustomAppBarShape extends OutlinedBorder { // Implementing the constructor allows the CustomAppBarShape to be // properly compared when calling the `identical` method. const CustomAppBarShape({ super.side }); Path _getPath(Rect rect) { final Path path = Path(); final Size size = Size(rect.width, rect.height * 1.5) ; final double p0 = size.height * 0.75; path.lineTo(0.0, p0); final Offset controlPoint = Offset(size.width * 0.4, size.height); final Offset endPoint = Offset(size.width, size.height / 2); path.quadraticBezierTo(controlPoint.dx, controlPoint.dy, endPoint.dx, endPoint.dy); path.lineTo(size.width, 0.0); path.close(); return path; } @override Path getOuterPath(Rect rect, {TextDirection? textDirection}) { return _getPath(rect.inflate(side.width)); } @override Path getInnerPath(Rect rect, {TextDirection? textDirection}) { return _getPath(rect); } @override void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) { if (rect.isEmpty) { return; } canvas.drawPath( getOuterPath(rect, textDirection: textDirection), side.toPaint(), ); } @override ShapeBorder scale(double t) { return CustomAppBarShape(side: side.scale(t)); } @override OutlinedBorder copyWith({BorderSide? side}) { return CustomAppBarShape(side: side ?? this.side); } // The lerpFrom method is necessary for the CustomAppBarShape to be // properly animated when changing the AppBar shape and when // the AppBar is rebuilt. @override ShapeBorder? lerpFrom(ShapeBorder? a, double t) { if (a is CustomAppBarShape) { return CustomAppBarShape(side: BorderSide.lerp(a.side, side, t)); } return super.lerpFrom(a, t); } // The lerpTo method is necessary for the CustomAppBarShape to be // properly animated when changing the AppBar shape and when // the AppBar is rebuilt. @override ShapeBorder? lerpTo(ShapeBorder? b, double t) { if (b is CustomAppBarShape) { return CustomAppBarShape(side: BorderSide.lerp(b.side, side, t)); } return super.lerpTo(b, t); } }