Initial code

This commit is contained in:
Shubham Soni 2022-01-24 15:20:31 +05:30
commit 8ef0d87c96
12 changed files with 703 additions and 0 deletions

75
.gitignore vendored Normal file
View File

@ -0,0 +1,75 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
build/
# Android related
**/android/**/gradle-wrapper.jar
**/android/.gradle
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Flutter.podspec
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/ephemeral
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/Flutter/flutter_export_environment.sh
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3

10
.metadata Normal file
View File

@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 18116933e77adc82f80866c928266a5b4f1ed645
channel: unknown
project_type: package

3
CHANGELOG.md Normal file
View File

@ -0,0 +1,3 @@
## 0.0.1
* TODO: Describe initial release.

1
LICENSE Normal file
View File

@ -0,0 +1 @@
TODO: Add your license here.

39
README.md Normal file
View File

@ -0,0 +1,39 @@
<!--
This README describes the package. If you publish this package to pub.dev,
this README's contents appear on the landing page for your package.
For information about how to write a good package README, see the guide for
[writing package pages](https://dart.dev/guides/libraries/writing-package-pages).
For general information about developing packages, see the Dart guide for
[creating packages](https://dart.dev/guides/libraries/create-library-packages)
and the Flutter guide for
[developing packages and plugins](https://flutter.dev/developing-packages).
-->
TODO: Put a short description of the package here that helps potential users
know whether this package might be useful for them.
## Features
TODO: List what your package can do. Maybe include images, gifs, or videos.
## Getting started
TODO: List prerequisites and provide or point to information on how to
start using the package.
## Usage
TODO: Include short and useful examples for package users. Add longer examples
to `/example` folder.
```dart
const like = 'sample';
```
## Additional information
TODO: Tell users more about the package: where to find more information, how to
contribute to the package, how to file issues, what response they can expect
from the package authors, and more.

4
analysis_options.yaml Normal file
View File

@ -0,0 +1,4 @@
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View File

@ -0,0 +1,5 @@
library autoscale_tabbarview;
export 'src/autoscale_tabbar_widget.dart';
export 'src/sized_pageview.dart';
export 'src/size_detector_widget.dart';

View File

@ -0,0 +1,228 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'sized_pageview.dart';
class AutoScaleTabBarView extends StatefulWidget {
/// Creates a page view with one child per tab.
///
/// The length of [children] must be the same as the [controller]'s length.
const AutoScaleTabBarView({
Key? key,
required this.children,
this.controller,
this.physics,
this.dragStartBehavior = DragStartBehavior.start,
}) : assert(children != null),
assert(dragStartBehavior != null),
super(key: key);
/// This widget's selection and animation state.
///
/// If [TabController] is not provided, then the value of [DefaultTabController.of]
/// will be used.
final TabController? controller;
/// One widget per tab.
///
/// Its length must match the length of the [TabBar.tabs]
/// list, as well as the [controller]'s [TabController.length].
final List<Widget> children;
/// How the page view should respond to user input.
///
/// For example, determines how the page view continues to animate after the
/// user stops dragging the page view.
///
/// The physics are modified to snap to page boundaries using
/// [PageScrollPhysics] prior to being used.
///
/// Defaults to matching platform conventions.
final ScrollPhysics? physics;
/// {@macro flutter.widgets.scrollable.dragStartBehavior}
final DragStartBehavior dragStartBehavior;
@override
State<AutoScaleTabBarView> createState() => _AutoScaleTabBarViewState();
}
class _AutoScaleTabBarViewState extends State<AutoScaleTabBarView> {
TabController? _controller;
late PageController _pageController;
late List<Widget> _children;
late List<Widget> _childrenWithKey;
int? _currentIndex;
int _warpUnderwayCount = 0;
// If the TabBarView is rebuilt with a new tab controller, the caller should
// dispose the old one. In that case the old controller's animation will be
// null and should not be accessed.
bool get _controllerIsValid => _controller?.animation != null;
void _updateTabController() {
final TabController? newController =
widget.controller ?? DefaultTabController.of(context);
assert(() {
if (newController == null) {
throw FlutterError(
'No TabController for ${widget.runtimeType}.\n'
'When creating a ${widget.runtimeType}, you must either provide an explicit '
'TabController using the "controller" property, or you must ensure that there '
'is a DefaultTabController above the ${widget.runtimeType}.\n'
'In this case, there was neither an explicit controller nor a default controller.',
);
}
return true;
}());
if (newController == _controller) return;
if (_controllerIsValid)
_controller!.animation!.removeListener(_handleTabControllerAnimationTick);
_controller = newController;
if (_controller != null)
_controller!.animation!.addListener(_handleTabControllerAnimationTick);
}
@override
void initState() {
super.initState();
_updateChildren();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
_updateTabController();
_currentIndex = _controller?.index;
_pageController = PageController(initialPage: _currentIndex ?? 0);
}
@override
void didUpdateWidget(AutoScaleTabBarView oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.controller != oldWidget.controller) _updateTabController();
if (widget.children != oldWidget.children && _warpUnderwayCount == 0)
_updateChildren();
}
@override
void dispose() {
if (_controllerIsValid)
_controller!.animation!.removeListener(_handleTabControllerAnimationTick);
_controller = null;
// We don't own the _controller Animation, so it's not disposed here.
super.dispose();
}
void _updateChildren() {
_children = widget.children;
_childrenWithKey = KeyedSubtree.ensureUniqueKeysForList(widget.children);
}
void _handleTabControllerAnimationTick() {
if (_warpUnderwayCount > 0 || !_controller!.indexIsChanging)
return; // This widget is driving the controller's animation.
if (_controller!.index != _currentIndex) {
_currentIndex = _controller!.index;
_warpToCurrentIndex();
}
}
Future<void> _warpToCurrentIndex() async {
if (!mounted) return Future<void>.value();
if (_pageController.page == _currentIndex!.toDouble())
return Future<void>.value();
final int previousIndex = _controller!.previousIndex;
if ((_currentIndex! - previousIndex).abs() == 1) {
_warpUnderwayCount += 1;
await _pageController.animateToPage(_currentIndex!,
duration: kTabScrollDuration, curve: Curves.ease);
_warpUnderwayCount -= 1;
return Future<void>.value();
}
assert((_currentIndex! - previousIndex).abs() > 1);
final int initialPage = _currentIndex! > previousIndex
? _currentIndex! - 1
: _currentIndex! + 1;
final List<Widget> originalChildren = _childrenWithKey;
setState(() {
_warpUnderwayCount += 1;
_childrenWithKey = List<Widget>.from(_childrenWithKey, growable: false);
final Widget temp = _childrenWithKey[initialPage];
_childrenWithKey[initialPage] = _childrenWithKey[previousIndex];
_childrenWithKey[previousIndex] = temp;
});
_pageController.jumpToPage(initialPage);
await _pageController.animateToPage(_currentIndex!,
duration: kTabScrollDuration, curve: Curves.ease);
if (!mounted) return Future<void>.value();
setState(() {
_warpUnderwayCount -= 1;
if (widget.children != _children) {
_updateChildren();
} else {
_childrenWithKey = originalChildren;
}
});
}
// Called when the PageView scrolls
bool _handleScrollNotification(ScrollNotification notification) {
if (_warpUnderwayCount > 0) return false;
if (notification.depth != 0) return false;
_warpUnderwayCount += 1;
if (notification is ScrollUpdateNotification &&
!_controller!.indexIsChanging) {
if ((_pageController.page! - _controller!.index).abs() > 1.0) {
_controller!.index = _pageController.page!.floor();
_currentIndex = _controller!.index;
}
_controller!.offset =
(_pageController.page! - _controller!.index).clamp(-1.0, 1.0);
} else if (notification is ScrollEndNotification) {
_controller!.index = _pageController.page!.round();
_currentIndex = _controller!.index;
if (!_controller!.indexIsChanging) {
_controller!.offset =
(_pageController.page! - _controller!.index).clamp(-1.0, 1.0);
}
}
_warpUnderwayCount -= 1;
return false;
}
@override
Widget build(BuildContext context) {
assert(() {
if (_controller!.length != widget.children.length) {
throw FlutterError(
"Controller's length property (${_controller!.length}) does not match the "
"number of tabs (${widget.children.length}) present in TabBar's tabs property.",
);
}
return true;
}());
return NotificationListener<ScrollNotification>(
onNotification: _handleScrollNotification,
child: SizedPageView(
dragStartBehavior: widget.dragStartBehavior,
pageController: _pageController,
physics: widget.physics == null
? const PageScrollPhysics().applyTo(const ClampingScrollPhysics())
: const PageScrollPhysics().applyTo(widget.physics),
children: _childrenWithKey,
),
);
}
}

View File

@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
class SizeDetectorWidget extends StatefulWidget {
final Widget child;
final ValueChanged<Size> onSizeDetect;
const SizeDetectorWidget({
Key? key,
required this.child,
required this.onSizeDetect,
}) : super(key: key);
@override
_SizeDetectorWidgetState createState() => _SizeDetectorWidgetState();
}
class _SizeDetectorWidgetState extends State<SizeDetectorWidget> {
Size? _oldSize;
@override
void initState() {
super.initState();
SchedulerBinding.instance?.addPostFrameCallback((_) => _detectSize());
}
@override
Widget build(BuildContext context) {
return widget.child;
}
void _detectSize() {
if (!mounted) {
return;
}
final size = context.size;
if (_oldSize != size) {
_oldSize = size;
widget.onSizeDetect(size!);
}
}
}

View File

@ -0,0 +1,81 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'size_detector_widget.dart';
class SizedPageView extends StatefulWidget {
final List<Widget> children;
final PageController pageController;
final DragStartBehavior dragStartBehavior;
final ScrollPhysics physics;
const SizedPageView({
Key? key,
required this.children,
required this.pageController,
required this.dragStartBehavior,
required this.physics,
}) : super(key: key);
@override
_SizedPageViewState createState() => _SizedPageViewState();
}
class _SizedPageViewState extends State<SizedPageView>
with SingleTickerProviderStateMixin {
late List<double> _heights;
int _currentIndex = 0;
double get _currentHeight => _heights[_currentIndex];
@override
void initState() {
super.initState();
_heights = List.generate(widget.children.length, (index) => 0.0);
widget.pageController.addListener(() {
final _newIndex = widget.pageController.page?.round();
if (_currentIndex != _newIndex) {
if (!mounted) {
return;
}
setState(() => _currentIndex = _newIndex!);
}
});
}
@override
Widget build(BuildContext context) {
return TweenAnimationBuilder<double>(
curve: Curves.easeInOutCubic,
duration: const Duration(milliseconds: 100),
tween: Tween<double>(begin: _heights[0], end: _currentHeight),
builder: (context, value, child) => SizedBox(height: value, child: child),
child: PageView(
controller: widget.pageController,
physics: widget.physics,
dragStartBehavior: widget.dragStartBehavior,
children: List.generate(widget.children.length, (index) {
return OverflowBox(
maxHeight: double.infinity,
alignment: Alignment.topCenter,
child: SizeDetectorWidget(
onSizeDetect: (size) {
if (mounted) {
setState(() => _heights[index] = size.height);
}
},
child: Align(child: widget.children[index]),
),
);
}),
),
);
}
@override
void dispose() {
widget.pageController.dispose();
super.dispose();
}
}

161
pubspec.lock Normal file
View File

@ -0,0 +1,161 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.8.1"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
lints:
dependency: transitive
description:
name: lints
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.10"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.2"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
sdks:
dart: ">=2.12.0 <3.0.0"
flutter: ">=1.17.0"

54
pubspec.yaml Normal file
View File

@ -0,0 +1,54 @@
name: autoscale_tabbarview
description: A new Flutter package project.
version: 0.0.1
homepage:
environment:
sdk: ">=2.12.0 <3.0.0"
flutter: ">=1.17.0"
dependencies:
flutter:
sdk: flutter
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^1.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# To add assets to your package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/assets-and-images/#from-packages
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# To add custom fonts to your package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/custom-fonts/#from-packages