import 'dart:async'; import 'package:flutter/material.dart'; import 'package:rxdart/subjects.dart'; double ourMap(v, start1, stop1, start2, stop2) { return (v - start1) / (stop1 - start1) * (stop2 - start2) + start2; } void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: HomePage(), ); } } class HomePage extends StatefulWidget { @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin { final int initPage = 0; PageController _pageController; List<String> tabs = ['aaa', 'bbb', 'ccc', 'ddd', 'eee']; Stream<int> get currentPage$ => _currentPageSubject.stream; Sink<int> get currentPageSink => _currentPageSubject.sink; BehaviorSubject<int> _currentPageSubject; Alignment _dragAlignment; AnimationController _controller; Animation<Alignment> _animation; @override void initState() { super.initState(); _currentPageSubject = BehaviorSubject<int>.seeded(initPage); _pageController = PageController(initialPage: initPage); _dragAlignment = Alignment(ourMap(initPage, 0, tabs.length - 1, -1, 1), 0); _controller = AnimationController( vsync: this, duration: kThemeAnimationDuration, )..addListener(() { setState(() { _dragAlignment = _animation.value; }); }); currentPage$.listen((int page) { _runAnimation( _dragAlignment, Alignment(ourMap(page, 0, tabs.length - 1, -1, 1), 0), ); }); } @override void dispose() { _currentPageSubject.close(); _pageController.dispose(); _controller.dispose(); super.dispose(); } void _runAnimation(Alignment oldA, Alignment newA) { _animation = _controller.drive( AlignmentTween( begin: oldA, end: newA, ), ); _controller.reset(); _controller.forward(); } @override Widget build(BuildContext context) { return Scaffold( body: Column( children: <Widget>[ SizedBox(height: MediaQuery.of(context).padding.top + 20), Padding( padding: const EdgeInsets.symmetric(horizontal: 12.0), child: Container( height: 40, decoration: BoxDecoration( color: Colors.black38, borderRadius: BorderRadius.circular(35), ), child: Stack( children: <Widget>[ // use animation controller // Align( // alignment: _dragAlignment, // child: LayoutBuilder( // builder: // (BuildContext context, BoxConstraints constraints) { // double width = constraints.maxWidth; // return Padding( // padding: const EdgeInsets.all(2.0), // child: Container( // height: double.infinity, // width: width / tabs.length, // decoration: BoxDecoration( // color: Colors.white, // borderRadius: BorderRadius.circular(35), // ), // ), // ); // }, // ), // ), // use animated widget StreamBuilder( stream: currentPage$, builder: (context, AsyncSnapshot<int> snapshot) { if (snapshot.connectionState == ConnectionState.active) { return AnimatedAlign( duration: kThemeAnimationDuration, alignment: Alignment( ourMap(snapshot.data, 0, tabs.length - 1, -1, 1), 0), child: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { double width = constraints.maxWidth; return Padding( padding: const EdgeInsets.all(2.0), child: Container( height: double.infinity, width: width / tabs.length, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(35), ), ), ); }, ), ); } return SizedBox(); }, ), Align( alignment: Alignment.center, child: Row( children: tabs.map((t) { int index = tabs.indexOf(t); return Expanded( child: MaterialButton( splashColor: Colors.transparent, focusColor: Colors.transparent, color: Colors.transparent, highlightColor: Colors.transparent, hoverColor: Colors.transparent, focusElevation: 0.0, hoverElevation: 0.0, elevation: 0.0, highlightElevation: 0.0, child: StreamBuilder( stream: currentPage$, builder: (context, AsyncSnapshot<int> snapshot) { return AnimatedDefaultTextStyle( duration: kThemeAnimationDuration, style: TextStyle( inherit: true, color: snapshot.data == index ? Colors.black : Colors.white, ), child: Text(t), ); }), onPressed: () { currentPageSink.add(index); _pageController.jumpToPage(index); }, ), ); }).toList(), ), ), ], ), ), ), Expanded( child: PageView( controller: _pageController, onPageChanged: (page) => currentPageSink.add(page), children: <Widget>[ for (var t in tabs) Center( child: Text(t), ) ], ), ), ], ), ); } }


source: https://www.cnblogs.com/ajanuw/p/11456688.html