import 'dart:io'; import 'package:draggable_menu/draggable_menu.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mangayomi/router/router.dart'; import 'package:mangayomi/utils/extensions/build_context_extensions.dart'; class MeasureWidgetSize extends StatefulWidget { final Function(Size? size) onCalculateSize; final Widget child; const MeasureWidgetSize({super.key, required this.onCalculateSize, required this.child}); @override State createState() => _MeasureWidgetSizeState(); } class _MeasureWidgetSizeState extends State { final _key = GlobalKey(); @override initState() { WidgetsBinding.instance.addPostFrameCallback((_) => widget.onCalculateSize(_key.currentContext?.size)); super.initState(); } @override Widget build(BuildContext context) { return Container(key: _key, child: widget.child); } } Future customDraggableTabBar( {required List tabs, required List children, required BuildContext context, required TickerProvider vsync, bool fullWidth = false, Widget? moreWidget}) async { final controller = DraggableMenuController(); late TabController tabBarController; tabBarController = TabController(length: tabs.length, vsync: vsync); final maxHeight = context.height(0.8); int index = 0; List> widgetsHeight = []; void refresh() { controller.animateTo(widgetsHeight.indexWhere((element) => element["index"] == index)); } tabBarController.animation!.addListener(() { final currentIndex = tabBarController.animation!.value.round(); index = tabBarController.index; if (index != currentIndex) { index = currentIndex; refresh(); } else { refresh(); } }); await showDialog( context: context, builder: (context) { return Material( child: Column( children: [ for (var i = 0; i < children.length; i++) ...[ MeasureWidgetSize( onCalculateSize: (size) { final additionnalHeight = ((List.generate(10000, (index) => index * 0.0001))..shuffle()).first; double newHeight = size!.height + 52.0 + additionnalHeight; if (!(newHeight <= maxHeight)) { newHeight = maxHeight + additionnalHeight; } widgetsHeight.add({"index": i, "height": newHeight}); if (widgetsHeight.length == children.length) { Navigator.pop(context); } }, child: children[i]) ] ], ), ); }, ); widgetsHeight.sort((a, b) => (a["height"] as double).compareTo(b["height"] as double)); if (context.mounted) { await DraggableMenu.open( context, DraggableMenu( curve: Curves.linearToEaseOut, controller: controller, levels: widgetsHeight.map((e) => e["height"]).map((e) => DraggableMenuLevel(height: e)).toList(), customUi: Consumer(builder: (context, ref, child) { final location = ref.watch(routerCurrentLocationStateProvider(context)); final width = context.isTablet && !fullWidth ? switch (location) { null => 100, != '/MangaLibrary' && != '/AnimeLibrary' && != '/history' && != '/browse' && != '/more' => 0, _ => 100, } : 0; return Scaffold( backgroundColor: Platform.isLinux ? null : Colors.transparent, body: Container( width: context.width(1) - width, decoration: BoxDecoration( borderRadius: const BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20)), color: Theme.of(context).scaffoldBackgroundColor), child: DefaultTabController( length: tabs.length, child: Column(children: [ Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ Flexible( flex: 9, child: TabBar( unselectedLabelStyle: const TextStyle(fontWeight: FontWeight.w500), labelStyle: const TextStyle(fontWeight: FontWeight.bold), dividerColor: context.isLight ? Colors.black : Colors.grey, dividerHeight: 0.4, controller: tabBarController, tabs: tabs), ), if (moreWidget != null) Flexible( flex: 1, child: Column( children: [ moreWidget, const SizedBox(height: 2), Row( children: [ Flexible( child: Container(color: context.isLight ? Colors.black : Colors.grey, height: 0.4), ), ], ) ], ), ) ], ), Flexible( child: TabBarView( controller: tabBarController, children: children .map((e) => SingleChildScrollView( child: MeasureWidgetSize(onCalculateSize: (_) => refresh(), child: e))) .toList())) ]), ), ), ); }), child: const SizedBox.shrink())); } }