mirror of
https://github.com/AppFlowy-IO/AppFlowy
synced 2026-05-24 09:38:25 +00:00
Merge pull request #1004 from AppFlowy-IO/fix/board_ui_bugs
Fix/board UI bugs
This commit is contained in:
commit
69da9acd8b
12 changed files with 114 additions and 60 deletions
|
|
@ -21,6 +21,9 @@ class BoardCheckboxCellBloc
|
||||||
didReceiveCellUpdate: (cellData) {
|
didReceiveCellUpdate: (cellData) {
|
||||||
emit(state.copyWith(isSelected: _isSelected(cellData)));
|
emit(state.copyWith(isSelected: _isSelected(cellData)));
|
||||||
},
|
},
|
||||||
|
select: () async {
|
||||||
|
cellController.saveCellData(!state.isSelected ? "Yes" : "No");
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -50,6 +53,7 @@ class BoardCheckboxCellBloc
|
||||||
@freezed
|
@freezed
|
||||||
class BoardCheckboxCellEvent with _$BoardCheckboxCellEvent {
|
class BoardCheckboxCellEvent with _$BoardCheckboxCellEvent {
|
||||||
const factory BoardCheckboxCellEvent.initial() = _InitialCell;
|
const factory BoardCheckboxCellEvent.initial() = _InitialCell;
|
||||||
|
const factory BoardCheckboxCellEvent.select() = _Selected;
|
||||||
const factory BoardCheckboxCellEvent.didReceiveCellUpdate(
|
const factory BoardCheckboxCellEvent.didReceiveCellUpdate(
|
||||||
String cellContent) = _DidReceiveCellUpdate;
|
String cellContent) = _DidReceiveCellUpdate;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,7 @@ class _BoardContentState extends State<BoardContent> {
|
||||||
|
|
||||||
Widget _buildCard(
|
Widget _buildCard(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
AppFlowyGroupData column,
|
AppFlowyGroupData group,
|
||||||
AppFlowyGroupItem columnItem,
|
AppFlowyGroupItem columnItem,
|
||||||
) {
|
) {
|
||||||
final boardColumnItem = columnItem as BoardColumnItem;
|
final boardColumnItem = columnItem as BoardColumnItem;
|
||||||
|
|
@ -242,10 +242,11 @@ class _BoardContentState extends State<BoardContent> {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
ValueKey? key = cardKeysCache[columnItem.id];
|
final groupItemId = columnItem.id + group.id;
|
||||||
|
ValueKey? key = cardKeysCache[groupItemId];
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
final newKey = ValueKey(columnItem.id);
|
final newKey = ValueKey(groupItemId);
|
||||||
cardKeysCache[columnItem.id] = newKey;
|
cardKeysCache[groupItemId] = newKey;
|
||||||
key = newKey;
|
key = newKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -255,7 +256,7 @@ class _BoardContentState extends State<BoardContent> {
|
||||||
decoration: _makeBoxDecoration(context),
|
decoration: _makeBoxDecoration(context),
|
||||||
child: BoardCard(
|
child: BoardCard(
|
||||||
gridId: gridId,
|
gridId: gridId,
|
||||||
groupId: column.id,
|
groupId: group.id,
|
||||||
fieldId: boardColumnItem.fieldContext.id,
|
fieldId: boardColumnItem.fieldContext.id,
|
||||||
isEditing: isEditing,
|
isEditing: isEditing,
|
||||||
cellBuilder: cellBuilder,
|
cellBuilder: cellBuilder,
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,9 @@ class _BoardCheckboxCellState extends State<BoardCheckboxCell> {
|
||||||
iconPadding: EdgeInsets.zero,
|
iconPadding: EdgeInsets.zero,
|
||||||
icon: icon,
|
icon: icon,
|
||||||
width: 20,
|
width: 20,
|
||||||
|
onPressed: () => context
|
||||||
|
.read<BoardCheckboxCellBloc>()
|
||||||
|
.add(const BoardCheckboxCellEvent.select()),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
|
||||||
_startListening();
|
_startListening();
|
||||||
},
|
},
|
||||||
select: () async {
|
select: () async {
|
||||||
_updateCellData();
|
cellController.saveCellData(!state.isSelected ? "Yes" : "No");
|
||||||
},
|
},
|
||||||
didReceiveCellUpdate: (cellData) {
|
didReceiveCellUpdate: (cellData) {
|
||||||
emit(state.copyWith(isSelected: _isSelected(cellData)));
|
emit(state.copyWith(isSelected: _isSelected(cellData)));
|
||||||
|
|
@ -49,10 +49,6 @@ class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateCellData() {
|
|
||||||
cellController.saveCellData(!state.isSelected ? "Yes" : "No");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
|
|
|
||||||
|
|
@ -103,11 +103,19 @@ class SelectOptionCellEditorBloc
|
||||||
void _filterOption(String optionName, Emitter<SelectOptionEditorState> emit) {
|
void _filterOption(String optionName, Emitter<SelectOptionEditorState> emit) {
|
||||||
final _MakeOptionResult result =
|
final _MakeOptionResult result =
|
||||||
_makeOptions(Some(optionName), state.allOptions);
|
_makeOptions(Some(optionName), state.allOptions);
|
||||||
emit(state.copyWith(
|
if (optionName.isEmpty) {
|
||||||
filter: Some(optionName),
|
emit(state.copyWith(
|
||||||
options: result.options,
|
filter: Some(optionName),
|
||||||
createOption: result.createOption,
|
options: result.options,
|
||||||
));
|
createOption: none(),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
emit(state.copyWith(
|
||||||
|
filter: Some(optionName),
|
||||||
|
options: result.options,
|
||||||
|
createOption: result.createOption,
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _loadOptions() {
|
void _loadOptions() {
|
||||||
|
|
|
||||||
|
|
@ -100,11 +100,7 @@ class SelectOptionTag extends StatelessWidget {
|
||||||
backgroundColor: color,
|
backgroundColor: color,
|
||||||
labelPadding: const EdgeInsets.symmetric(horizontal: 6),
|
labelPadding: const EdgeInsets.symmetric(horizontal: 6),
|
||||||
selected: true,
|
selected: true,
|
||||||
onSelected: (_) {
|
onSelected: (_) => onSelected?.call(),
|
||||||
if (onSelected != null) {
|
|
||||||
onSelected!();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import '../../header/type_option/select_option_editor.dart';
|
||||||
import 'extension.dart';
|
import 'extension.dart';
|
||||||
import 'text_field.dart';
|
import 'text_field.dart';
|
||||||
|
|
||||||
const double _editorPannelWidth = 300;
|
const double _editorPanelWidth = 300;
|
||||||
|
|
||||||
class SelectOptionCellEditor extends StatelessWidget with FlowyOverlayDelegate {
|
class SelectOptionCellEditor extends StatelessWidget with FlowyOverlayDelegate {
|
||||||
final GridSelectOptionCellController cellController;
|
final GridSelectOptionCellController cellController;
|
||||||
|
|
@ -75,8 +75,8 @@ class SelectOptionCellEditor extends StatelessWidget with FlowyOverlayDelegate {
|
||||||
//
|
//
|
||||||
FlowyOverlay.of(context).insertWithAnchor(
|
FlowyOverlay.of(context).insertWithAnchor(
|
||||||
widget: OverlayContainer(
|
widget: OverlayContainer(
|
||||||
constraints: BoxConstraints.loose(const Size(_editorPannelWidth, 300)),
|
constraints: BoxConstraints.loose(const Size(_editorPanelWidth, 300)),
|
||||||
child: SizedBox(width: _editorPannelWidth, child: editor),
|
child: SizedBox(width: _editorPanelWidth, child: editor),
|
||||||
),
|
),
|
||||||
identifier: SelectOptionCellEditor.identifier(),
|
identifier: SelectOptionCellEditor.identifier(),
|
||||||
anchorContext: context,
|
anchorContext: context,
|
||||||
|
|
@ -161,7 +161,7 @@ class _TextField extends StatelessWidget {
|
||||||
child: SelectOptionTextField(
|
child: SelectOptionTextField(
|
||||||
options: state.options,
|
options: state.options,
|
||||||
selectedOptionMap: optionMap,
|
selectedOptionMap: optionMap,
|
||||||
distanceToText: _editorPannelWidth * 0.7,
|
distanceToText: _editorPanelWidth * 0.7,
|
||||||
tagController: _tagController,
|
tagController: _tagController,
|
||||||
onClick: () => FlowyOverlay.of(context)
|
onClick: () => FlowyOverlay.of(context)
|
||||||
.remove(SelectOptionTypeOptionEditor.identifier),
|
.remove(SelectOptionTypeOptionEditor.identifier),
|
||||||
|
|
|
||||||
|
|
@ -32,10 +32,10 @@ class SelectOptionTextField extends StatelessWidget {
|
||||||
required this.onNewTag,
|
required this.onNewTag,
|
||||||
required this.newText,
|
required this.newText,
|
||||||
this.onClick,
|
this.onClick,
|
||||||
TextEditingController? controller,
|
TextEditingController? textController,
|
||||||
FocusNode? focusNode,
|
FocusNode? focusNode,
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : _controller = controller ?? TextEditingController(),
|
}) : _controller = textController ?? TextEditingController(),
|
||||||
_focusNode = focusNode ?? FocusNode(),
|
_focusNode = focusNode ?? FocusNode(),
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
|
|
@ -48,7 +48,7 @@ class SelectOptionTextField extends StatelessWidget {
|
||||||
textfieldTagsController: tagController,
|
textfieldTagsController: tagController,
|
||||||
initialTags: selectedOptionMap.keys.toList(),
|
initialTags: selectedOptionMap.keys.toList(),
|
||||||
focusNode: _focusNode,
|
focusNode: _focusNode,
|
||||||
textSeparators: const [' ', ','],
|
textSeparators: const [','],
|
||||||
inputfieldBuilder: (BuildContext context, editController, focusNode,
|
inputfieldBuilder: (BuildContext context, editController, focusNode,
|
||||||
error, onChanged, onSubmitted) {
|
error, onChanged, onSubmitted) {
|
||||||
return ((context, sc, tags, onTagDelegate) {
|
return ((context, sc, tags, onTagDelegate) {
|
||||||
|
|
@ -70,6 +70,7 @@ class SelectOptionTextField extends StatelessWidget {
|
||||||
|
|
||||||
if (text.isNotEmpty) {
|
if (text.isNotEmpty) {
|
||||||
onNewTag(text);
|
onNewTag(text);
|
||||||
|
focusNode.requestFocus();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,14 @@ import 'field_type_option_editor.dart';
|
||||||
class FieldEditor extends StatefulWidget {
|
class FieldEditor extends StatefulWidget {
|
||||||
final String gridId;
|
final String gridId;
|
||||||
final String fieldName;
|
final String fieldName;
|
||||||
|
final VoidCallback? onRemoved;
|
||||||
|
|
||||||
final IFieldTypeOptionLoader typeOptionLoader;
|
final IFieldTypeOptionLoader typeOptionLoader;
|
||||||
const FieldEditor({
|
const FieldEditor({
|
||||||
required this.gridId,
|
required this.gridId,
|
||||||
required this.fieldName,
|
this.fieldName = "",
|
||||||
required this.typeOptionLoader,
|
required this.typeOptionLoader,
|
||||||
|
this.onRemoved,
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ class _FieldNameTextFieldState extends State<FieldNameTextField> {
|
||||||
final theme = context.watch<AppTheme>();
|
final theme = context.watch<AppTheme>();
|
||||||
return RoundedInputField(
|
return RoundedInputField(
|
||||||
height: 36,
|
height: 36,
|
||||||
|
autoFocus: true,
|
||||||
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500),
|
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500),
|
||||||
controller: controller,
|
controller: controller,
|
||||||
normalBorderColor: theme.shader4,
|
normalBorderColor: theme.shader4,
|
||||||
|
|
@ -47,7 +48,8 @@ class _FieldNameTextFieldState extends State<FieldNameTextField> {
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(covariant FieldNameTextField oldWidget) {
|
void didUpdateWidget(covariant FieldNameTextField oldWidget) {
|
||||||
controller.text = widget.name;
|
controller.text = widget.name;
|
||||||
controller.selection = TextSelection.fromPosition(TextPosition(offset: controller.text.length));
|
controller.selection = TextSelection.fromPosition(
|
||||||
|
TextPosition(offset: controller.text.length));
|
||||||
|
|
||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,7 @@ class _PropertyList extends StatelessWidget {
|
||||||
axis: Axis.vertical,
|
axis: Axis.vertical,
|
||||||
controller: _scrollController,
|
controller: _scrollController,
|
||||||
barSize: GridSize.scrollBarSize,
|
barSize: GridSize.scrollBarSize,
|
||||||
|
autoHideScrollbar: false,
|
||||||
child: ListView.separated(
|
child: ListView.separated(
|
||||||
controller: _scrollController,
|
controller: _scrollController,
|
||||||
itemCount: state.gridCells.length,
|
itemCount: state.gridCells.length,
|
||||||
|
|
@ -132,7 +133,27 @@ class _PropertyList extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_CreateFieldButton(viewId: viewId),
|
_CreateFieldButton(
|
||||||
|
viewId: viewId,
|
||||||
|
onClosed: () {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
_scrollController.animateTo(
|
||||||
|
_scrollController.position.maxScrollExtent,
|
||||||
|
duration: const Duration(milliseconds: 250),
|
||||||
|
curve: Curves.ease,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onOpened: () {
|
||||||
|
return OverlayContainer(
|
||||||
|
constraints: BoxConstraints.loose(const Size(240, 200)),
|
||||||
|
child: FieldEditor(
|
||||||
|
gridId: viewId,
|
||||||
|
typeOptionLoader: NewFieldTypeOptionLoader(gridId: viewId),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -142,7 +163,14 @@ class _PropertyList extends StatelessWidget {
|
||||||
|
|
||||||
class _CreateFieldButton extends StatelessWidget {
|
class _CreateFieldButton extends StatelessWidget {
|
||||||
final String viewId;
|
final String viewId;
|
||||||
const _CreateFieldButton({required this.viewId, Key? key}) : super(key: key);
|
final Widget Function() onOpened;
|
||||||
|
final VoidCallback onClosed;
|
||||||
|
const _CreateFieldButton({
|
||||||
|
required this.viewId,
|
||||||
|
required this.onOpened,
|
||||||
|
required this.onClosed,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -150,6 +178,8 @@ class _CreateFieldButton extends StatelessWidget {
|
||||||
|
|
||||||
return Popover(
|
return Popover(
|
||||||
triggerActions: PopoverTriggerActionFlags.click,
|
triggerActions: PopoverTriggerActionFlags.click,
|
||||||
|
direction: PopoverDirection.bottomWithLeftAligned,
|
||||||
|
onClose: onClosed,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 40,
|
||||||
child: FlowyButton(
|
child: FlowyButton(
|
||||||
|
|
@ -162,16 +192,7 @@ class _CreateFieldButton extends StatelessWidget {
|
||||||
leftIcon: svgWidget("home/add"),
|
leftIcon: svgWidget("home/add"),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
popupBuilder: (BuildContext context) {
|
popupBuilder: (BuildContext context) => onOpened(),
|
||||||
return OverlayContainer(
|
|
||||||
constraints: BoxConstraints.loose(const Size(240, 200)),
|
|
||||||
child: FieldEditor(
|
|
||||||
gridId: viewId,
|
|
||||||
fieldName: "",
|
|
||||||
typeOptionLoader: NewFieldTypeOptionLoader(gridId: viewId),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ class StyledScrollbar extends StatefulWidget {
|
||||||
final ScrollController controller;
|
final ScrollController controller;
|
||||||
final Function(double)? onDrag;
|
final Function(double)? onDrag;
|
||||||
final bool showTrack;
|
final bool showTrack;
|
||||||
|
final bool autoHideScrollbar;
|
||||||
final Color? handleColor;
|
final Color? handleColor;
|
||||||
final Color? trackColor;
|
final Color? trackColor;
|
||||||
|
|
||||||
|
|
@ -30,6 +31,7 @@ class StyledScrollbar extends StatefulWidget {
|
||||||
this.onDrag,
|
this.onDrag,
|
||||||
this.contentSize,
|
this.contentSize,
|
||||||
this.showTrack = false,
|
this.showTrack = false,
|
||||||
|
this.autoHideScrollbar = true,
|
||||||
this.handleColor,
|
this.handleColor,
|
||||||
this.trackColor})
|
this.trackColor})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
@ -48,16 +50,13 @@ class ScrollbarState extends State<StyledScrollbar> {
|
||||||
widget.controller.addListener(() => setState(() {}));
|
widget.controller.addListener(() => setState(() {}));
|
||||||
widget.controller.position.isScrollingNotifier.addListener(
|
widget.controller.position.isScrollingNotifier.addListener(
|
||||||
() {
|
() {
|
||||||
if (!mounted) {
|
if (!mounted) return;
|
||||||
return;
|
if (!widget.autoHideScrollbar) return;
|
||||||
}
|
|
||||||
_hideScrollbarOperation?.cancel();
|
_hideScrollbarOperation?.cancel();
|
||||||
if (!widget.controller.position.isScrollingNotifier.value) {
|
if (!widget.controller.position.isScrollingNotifier.value) {
|
||||||
// scroll is stopped
|
|
||||||
_hideScrollbarOperation = CancelableOperation.fromFuture(
|
_hideScrollbarOperation = CancelableOperation.fromFuture(
|
||||||
Future.delayed(const Duration(seconds: 2), () {}),
|
Future.delayed(const Duration(seconds: 2), () {}),
|
||||||
).then((_) {
|
).then((_) {
|
||||||
// Opti: hide with animation
|
|
||||||
hideHandler = true;
|
hideHandler = true;
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
|
|
@ -103,7 +102,6 @@ class ScrollbarState extends State<StyledScrollbar> {
|
||||||
break;
|
break;
|
||||||
case Axis.horizontal:
|
case Axis.horizontal:
|
||||||
// Use supplied contentSize if we have it, otherwise just fallback to maxScrollExtents
|
// Use supplied contentSize if we have it, otherwise just fallback to maxScrollExtents
|
||||||
|
|
||||||
if (contentSize != null && contentSize > 0) {
|
if (contentSize != null && contentSize > 0) {
|
||||||
maxExtent = contentSize - constraints.maxWidth;
|
maxExtent = contentSize - constraints.maxWidth;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -118,7 +116,8 @@ class ScrollbarState extends State<StyledScrollbar> {
|
||||||
// Calculate the alignment for the handle, this is a value between 0 and 1,
|
// Calculate the alignment for the handle, this is a value between 0 and 1,
|
||||||
// it automatically takes the handle size into acct
|
// it automatically takes the handle size into acct
|
||||||
// ignore: omit_local_variable_types
|
// ignore: omit_local_variable_types
|
||||||
double handleAlignment = maxExtent == 0 ? 0 : widget.controller.offset / maxExtent;
|
double handleAlignment =
|
||||||
|
maxExtent == 0 ? 0 : widget.controller.offset / maxExtent;
|
||||||
|
|
||||||
// Convert handle alignment from [0, 1] to [-1, 1]
|
// Convert handle alignment from [0, 1] to [-1, 1]
|
||||||
handleAlignment *= 2.0;
|
handleAlignment *= 2.0;
|
||||||
|
|
@ -127,19 +126,25 @@ class ScrollbarState extends State<StyledScrollbar> {
|
||||||
// Calculate handleSize by comparing the total content size to our viewport
|
// Calculate handleSize by comparing the total content size to our viewport
|
||||||
var handleExtent = _viewExtent;
|
var handleExtent = _viewExtent;
|
||||||
if (contentExtent > _viewExtent) {
|
if (contentExtent > _viewExtent) {
|
||||||
//Make sure handle is never small than the minSize
|
// Make sure handle is never small than the minSize
|
||||||
handleExtent = max(60, _viewExtent * _viewExtent / contentExtent);
|
handleExtent = max(60, _viewExtent * _viewExtent / contentExtent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hide the handle if content is < the viewExtent
|
// Hide the handle if content is < the viewExtent
|
||||||
var showHandle = contentExtent > _viewExtent && contentExtent > 0;
|
var showHandle = contentExtent > _viewExtent && contentExtent > 0;
|
||||||
|
|
||||||
if (hideHandler) {
|
if (hideHandler) {
|
||||||
showHandle = false;
|
showHandle = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle color
|
// Handle color
|
||||||
var handleColor = widget.handleColor ?? (theme.isDark ? theme.bg2.withOpacity(.2) : theme.bg2);
|
var handleColor = widget.handleColor ??
|
||||||
|
(theme.isDark ? theme.bg2.withOpacity(.2) : theme.bg2);
|
||||||
// Track color
|
// Track color
|
||||||
var trackColor = widget.trackColor ?? (theme.isDark ? theme.bg2.withOpacity(.1) : theme.bg2.withOpacity(.3));
|
var trackColor = widget.trackColor ??
|
||||||
|
(theme.isDark
|
||||||
|
? theme.bg2.withOpacity(.1)
|
||||||
|
: theme.bg2.withOpacity(.3));
|
||||||
|
|
||||||
//Layout the stack, it just contains a child, and
|
//Layout the stack, it just contains a child, and
|
||||||
return Stack(children: <Widget>[
|
return Stack(children: <Widget>[
|
||||||
|
|
@ -149,8 +154,12 @@ class ScrollbarState extends State<StyledScrollbar> {
|
||||||
alignment: const Alignment(1, 1),
|
alignment: const Alignment(1, 1),
|
||||||
child: Container(
|
child: Container(
|
||||||
color: trackColor,
|
color: trackColor,
|
||||||
width: widget.axis == Axis.vertical ? widget.size : double.infinity,
|
width: widget.axis == Axis.vertical
|
||||||
height: widget.axis == Axis.horizontal ? widget.size : double.infinity,
|
? widget.size
|
||||||
|
: double.infinity,
|
||||||
|
height: widget.axis == Axis.horizontal
|
||||||
|
? widget.size
|
||||||
|
: double.infinity,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|
@ -167,10 +176,14 @@ class ScrollbarState extends State<StyledScrollbar> {
|
||||||
// HANDLE SHAPE
|
// HANDLE SHAPE
|
||||||
child: MouseHoverBuilder(
|
child: MouseHoverBuilder(
|
||||||
builder: (_, isHovered) => Container(
|
builder: (_, isHovered) => Container(
|
||||||
width: widget.axis == Axis.vertical ? widget.size : handleExtent,
|
width:
|
||||||
height: widget.axis == Axis.horizontal ? widget.size : handleExtent,
|
widget.axis == Axis.vertical ? widget.size : handleExtent,
|
||||||
|
height: widget.axis == Axis.horizontal
|
||||||
|
? widget.size
|
||||||
|
: handleExtent,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: handleColor.withOpacity(isHovered ? 1 : .85), borderRadius: Corners.s3Border),
|
color: handleColor.withOpacity(isHovered ? 1 : .85),
|
||||||
|
borderRadius: Corners.s3Border),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -182,15 +195,19 @@ class ScrollbarState extends State<StyledScrollbar> {
|
||||||
|
|
||||||
void _handleHorizontalDrag(DragUpdateDetails details) {
|
void _handleHorizontalDrag(DragUpdateDetails details) {
|
||||||
var pos = widget.controller.offset;
|
var pos = widget.controller.offset;
|
||||||
var pxRatio = (widget.controller.position.maxScrollExtent + _viewExtent) / _viewExtent;
|
var pxRatio = (widget.controller.position.maxScrollExtent + _viewExtent) /
|
||||||
widget.controller.jumpTo((pos + details.delta.dx * pxRatio).clamp(0.0, widget.controller.position.maxScrollExtent));
|
_viewExtent;
|
||||||
|
widget.controller.jumpTo((pos + details.delta.dx * pxRatio)
|
||||||
|
.clamp(0.0, widget.controller.position.maxScrollExtent));
|
||||||
widget.onDrag?.call(details.delta.dx);
|
widget.onDrag?.call(details.delta.dx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleVerticalDrag(DragUpdateDetails details) {
|
void _handleVerticalDrag(DragUpdateDetails details) {
|
||||||
var pos = widget.controller.offset;
|
var pos = widget.controller.offset;
|
||||||
var pxRatio = (widget.controller.position.maxScrollExtent + _viewExtent) / _viewExtent;
|
var pxRatio = (widget.controller.position.maxScrollExtent + _viewExtent) /
|
||||||
widget.controller.jumpTo((pos + details.delta.dy * pxRatio).clamp(0.0, widget.controller.position.maxScrollExtent));
|
_viewExtent;
|
||||||
|
widget.controller.jumpTo((pos + details.delta.dy * pxRatio)
|
||||||
|
.clamp(0.0, widget.controller.position.maxScrollExtent));
|
||||||
widget.onDrag?.call(details.delta.dy);
|
widget.onDrag?.call(details.delta.dy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -204,6 +221,7 @@ class ScrollbarListStack extends StatelessWidget {
|
||||||
final EdgeInsets? scrollbarPadding;
|
final EdgeInsets? scrollbarPadding;
|
||||||
final Color? handleColor;
|
final Color? handleColor;
|
||||||
final Color? trackColor;
|
final Color? trackColor;
|
||||||
|
final bool autoHideScrollbar;
|
||||||
|
|
||||||
const ScrollbarListStack(
|
const ScrollbarListStack(
|
||||||
{Key? key,
|
{Key? key,
|
||||||
|
|
@ -214,6 +232,7 @@ class ScrollbarListStack extends StatelessWidget {
|
||||||
this.contentSize,
|
this.contentSize,
|
||||||
this.scrollbarPadding,
|
this.scrollbarPadding,
|
||||||
this.handleColor,
|
this.handleColor,
|
||||||
|
this.autoHideScrollbar = true,
|
||||||
this.trackColor})
|
this.trackColor})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
|
|
@ -238,6 +257,7 @@ class ScrollbarListStack extends StatelessWidget {
|
||||||
contentSize: contentSize,
|
contentSize: contentSize,
|
||||||
trackColor: trackColor,
|
trackColor: trackColor,
|
||||||
handleColor: handleColor,
|
handleColor: handleColor,
|
||||||
|
autoHideScrollbar: autoHideScrollbar,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
// The animate will be used by the children that using styled_widget.
|
// The animate will be used by the children that using styled_widget.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue