mirror of
https://github.com/flutter/flutter.git
synced 2026-01-20 20:55:29 +08:00
<!-- Thanks for filing a pull request! Reviewers are typically assigned within a week of filing a request. To learn more about code review, see our documentation on Tree Hygiene: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md --> Adds a new property to InputDecoration `hintTextWidget` *Fixes*: #161130 With the introduction of ~hintTextWidget~ hint We should be able to animate hintText https://github.com/user-attachments/assets/7955a835-5f60-4451-8ede-b5e5f0457046 https://github.com/user-attachments/assets/55d7f021-cc8f-471e-a1d8-e601262ff640 <details> <summary>sample code</summary> ```dart import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({Key? key, required this.title}) : super(key: key); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin { String _searchText = 'Popular Picks'; List<String> wordsToShow = [ 'Popular Picks', 'Trending', 'New Arrivals', 'Best Sellers', 'Top Rated', ]; int index = 0; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Padding( padding: const EdgeInsets.all(8.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ TextField( cursorHeight: 16, decoration: InputDecoration( maintainHintHeight: true, hintTextWidget: Row( children: [ const Text( "Search for ", style: TextStyle(fontSize: 16), ), SlidingText( onCompleted: () { index = (index + 1) % wordsToShow.length; setState(() { _searchText = wordsToShow[index]; }); }, word: _searchText, interval: 1500, isDelay: true), ], ), border: OutlineInputBorder(), ), ), ], ), ), ), ); } } class SlidingText extends StatefulWidget { final String word; final int interval; final bool isDelay; final Function onCompleted; const SlidingText({ required this.word, required this.interval, required this.isDelay, required this.onCompleted, Key? key, }) : super(key: key); @override _SlidingTextState createState() => _SlidingTextState(); } class _SlidingTextState extends State<SlidingText> with SingleTickerProviderStateMixin { late AnimationController _animController; late Animation<Offset> _slideAnimation; late Animation<double> _fadeAnimation; @override void initState() { super.initState(); _animController = AnimationController( duration: Duration(milliseconds: widget.interval), vsync: this, ); _slideAnimation = Tween<Offset>( begin: Offset(0, 0.5), end: Offset(0, -0.6), ).animate( CurvedAnimation( parent: _animController, curve: Curves.easeInOut, ), ); _fadeAnimation = TweenSequence([ TweenSequenceItem( tween: Tween<double>(begin: 0.0, end: 1.0).chain( CurveTween(curve: Curves.easeIn), ), weight: 50, // First half of the animation ), TweenSequenceItem( tween: Tween<double>(begin: 1.0, end: 0.0).chain( CurveTween(curve: Curves.easeOut), ), weight: 50, // Second half of the animation ), ]).animate(_animController); // add interval _animController.addStatusListener((status) async { if (status == AnimationStatus.completed) { await Future.delayed(Duration(milliseconds: 500)); widget.onCompleted(); } }); _animController.forward(); } @override void didUpdateWidget(covariant SlidingText oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.word != widget.word) { _animController.reset(); _animController.forward(); } } @override void dispose() { _animController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _animController, builder: (context, child) { return SlideTransition( position: _slideAnimation, child: FadeTransition( opacity: _fadeAnimation, child: Text( widget.word, style: const TextStyle(fontSize: 16), ), ), ); }, ); } } ``` </details> ## Pre-launch Checklist - [X] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [X] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [X] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [X] I signed the [CLA]. - [X] I listed at least one issue that this PR fixes in the description above. - [X] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [X] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [X] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md