#flutter #flutter-layout
Вопрос:
я пытаюсь использовать ползунок().. деления выглядят хорошо (значение >= 50 ? 10 : 20)
Но как добавить галочку и метку над слайдером ?
Ожидать :
галочка будет менять цвет в соответствии с положением ползунка
Фактический:
Slider(
min: 0,
max: 100,
value: value,
onChanged: (val) {
setState(() {
value = val;
});
},
divisions: value >= 50 ? 10 : 20,
label: value.toString(),
),
моя проблема в том, что :
- положение метки и галочки (при использовании столбца)
- Измените цвет галочки, если положение ползунка одинаковое
мой код с использованием столбца(
Column(
children: [
Container(
margin: EdgeInsets.symmetric(horizontal: 20),
child:
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(6, (index) => Text('$index')),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(
16,
(index) => SizedBox(
height: 8,
child: VerticalDivider(
width: 8,
color: HelperColors.orange,
),
),
),
)
],
),
),
Slider(
value: widget.value,
min: widget.minValue,
max: widget.maxValue,
divisions: widget.divisions,
onChanged: widget.onChanged,
label: widget.value.toString(),
),
],
);
не могли бы вы помочь мне решить некоторые проблемы с этим дизайном?
Ответ №1:
Расположение тиков и значений Тиков
Вместо того, чтобы заключать строки тиков и значений тиков в столбец, может быть лучше объединить как тик, так и значение тика в один столбец. Затем оберните этот столбец Expanded
виджетом и поместите его в List.generate
строку. Это гарантирует, что галочка всегда совпадает со значением галочки, и каждый столбец имеет одинаковый интервал.
Галочки и Выравнивание ползунка
Ползунок по умолчанию имеет смещение, чтобы освободить место для большого пальца и наложения. Даже если вы точно рассчитаете расстояние между галочками на экране, оно все равно будет растягиваться при тестировании на экране другого размера.
Пожалуйста, проверьте эту ссылку, если вам интересно узнать больше о смещении/ марже слайдера.
Проблема смещения ползунка
Вот что вам нужно сделать, чтобы достичь идеального выравнивания
- Удалите смещение, создав пользовательский
trackShape
ползунок. - Измерьте смещение одного интервала между делениями и разделите его на 2.
Формула таковаMediaQuery.of(context).size.width / numOfTick / 2
- Оберните ползунок с нулевым смещением
Padding
с помощью виджета и используйте вычисленное смещение в качестве значения горизонтального заполнения.padding: EdgeInsets.symmetric(horizontal: offset),
offset V |---| .----------/ /------------. | 0 | | 100 | | |Ticks area | | | | | | | || .----------/ /------------.| | || | Slider area ||Screen edge | || '----------/ /-------------'|
- Now ticks and slider will always be perfectly aligned, edge-to-edge, regardless screen size.
Here’s the example. You may still want to improve the UI as per your requirements.
What’s supported
- Adjustable major and minor ticks
- Выделение галочки, когда значение совпадает
- Контроль точности значений
double value = 50;
double actualValue = 50;
double minValue = 0;
double maxValue = 100;
List<double> steps = [0,5,10,15,20,25,30,35,40,45,50,60,70,80,90,100];
// ...
CustomSlider(
minValue: minValue,
maxValue: maxValue,
value: value,
majorTick: 6,
minorTick: 2,
labelValuePrecision: 0,
tickValuePrecision: 0,
onChanged: (val) => setState(() {
value = val;
actualValue =
steps[(val / maxValue * (steps.length - 1)).ceil().toInt()];
print('Slider value (linear): $value');
print('Actual value (non-linear): $actualValue');
}),
activeColor: Colors.orange,
inactiveColor: Colors.orange.shade50,
linearStep: false,
steps: steps,
),
Пользовательский виджет слайдера
class CustomSlider extends StatelessWidget {
final double value;
final double minValue;
final double maxValue;
final int majorTick;
final int minorTick;
final Function(double)? onChanged;
final Color? activeColor;
final Color? inactiveColor;
final int labelValuePrecision;
final int tickValuePrecision;
final bool linearStep;
final List<double>? steps;
CustomSlider({
required this.value,
required this.minValue,
required this.maxValue,
required this.majorTick,
required this.minorTick,
required this.onChanged,
this.activeColor,
this.inactiveColor,
this.labelValuePrecision = 2,
this.tickValuePrecision = 1,
this.linearStep = true,
this.steps,
});
@override
Widget build(BuildContext context) {
final allocatedHeight = MediaQuery.of(context).size.height;
final allocatedWidth = MediaQuery.of(context).size.width;
final divisions = (majorTick - 1) * minorTick majorTick;
final double valueHeight =
allocatedHeight * 0.05 < 41 ? 41 : allocatedHeight * 0.05;
final double tickHeight =
allocatedHeight * 0.025 < 20 ? 20 : allocatedHeight * 0.025;
final labelOffset = allocatedWidth / divisions / 2;
return Column(
children: [
Row(
children: List.generate(
divisions,
(index) => Expanded(
child: Column(
children: [
Container(
alignment: Alignment.bottomCenter,
height: valueHeight,
child: index % (minorTick 1) == 0
? Text(
linearStep
? '${(index / (divisions - 1) * maxValue).toStringAsFixed(tickValuePrecision)}'
: '${(steps?[index])?.toStringAsFixed(tickValuePrecision)}',
style: TextStyle(
fontSize: 12,
),
textAlign: TextAlign.center,
)
: null,
),
Container(
alignment: Alignment.bottomCenter,
height: tickHeight,
child: VerticalDivider(
indent: index % (minorTick 1) == 0 ? 2 : 6,
thickness: 1.2,
color: (index / (divisions - 1)) * maxValue == value
? activeColor ?? Colors.orange
: Colors.grey.shade300,
),
),
],
),
),
),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: labelOffset),
child: SliderTheme(
data: SliderThemeData(
trackHeight:
allocatedHeight * 0.011 < 9 ? 9 : allocatedHeight * 0.011,
activeTickMarkColor: activeColor ?? Colors.orange,
inactiveTickMarkColor: inactiveColor ?? Colors.orange.shade50,
activeTrackColor: activeColor ?? Colors.orange,
inactiveTrackColor: inactiveColor ?? Colors.orange.shade50,
thumbColor: activeColor ?? Colors.orange,
overlayColor: activeColor == null
? Colors.orange.withOpacity(0.1)
: activeColor!.withOpacity(0.1),
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 12.0),
trackShape: CustomTrackShape(),
showValueIndicator: ShowValueIndicator.never,
valueIndicatorTextStyle: TextStyle(
fontSize: 12,
),
),
child: Slider(
value: value,
min: minValue,
max: maxValue,
divisions: divisions - 1,
onChanged: onChanged,
label: value.toStringAsFixed(labelValuePrecision),
),
),
),
],
);
}
}
class CustomTrackShape extends RoundedRectSliderTrackShape {
Rect getPreferredRect({
required RenderBox parentBox,
Offset offset = Offset.zero,
required SliderThemeData sliderTheme,
bool isEnabled = false,
bool isDiscrete = false,
}) {
final double trackHeight = sliderTheme.trackHeight!;
final double trackLeft = offset.dx;
final double trackTop =
offset.dy (parentBox.size.height - trackHeight) / 2;
final double trackWidth = parentBox.size.width;
return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
}
}
Тест
Редактирование (ползунок с нелинейными шагами)
Поскольку ползунок Flutter имеет линейное сопоставление значений и позиций, возможно, не стоит изменять значение ползунка на основе нелинейных значений. Однако этого все еще можно достичь, создав дополнительный диапазон значений для фактического значения, а затем сопоставив их вместе.
Вот шаги
- Создайте список, содержащий нелинейные значения (тот, который вы создали)
- Убедитесь, что длина списка совпадает с общим количеством галочек. В противном случае отображение будет неверным.
listLength = (majorTick - 1) * minorTick majorTick
Приведенный выше код был отредактирован для решения этой проблемы.
Тест
Сопоставленные значения
Start End
Slider value (linear) 0.00 6.67 13.33 ... 86.67 93.33 100.0
Actual value (non-linear) 0.00 5.00 10.00 ... 80.00 90.00 100.0
Комментарии:
1. боже, большое тебе спасибо
2. но я просто хочу знать … как отправить выбранное значение индекса thick в OnChanged ?.. у меня есть значение
values = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 60, 70, 80, 90, 100];
…. итак, я установил значение на верхнюю метку.. но я вижу (индекс) внутриList.generate
и снаружи слайдера().. поэтому я не могу передать SelectedIndex в OnChanged3. Область галочек — это просто украшение пользовательского интерфейса. Даже если вам удастся извлечь индекс и передать его родительскому виджету, в конечном итоге вы измените поведение слайдера. Основная причина заключается в том, что значение и положение ползунка отображаются линейно. Между тем, шаги элементов в списке не являются линейными. Итак, один из способов добиться этого-создать 2 диапазона значений, один для слайдера и один для фактического значения. Оба должны иметь одинаковую длину для правильного сопоставления. Но будьте очень осторожны, потому что он подвержен нежелательным ошибкам, если он неправильно отображен. Более подробная информация в разделе ответов.
4. хммм.. мой лидер говорит … я должен настроить слайдер, чтобы исправить это.. я должен создать галочку аналогичного значения внутри списка из BE… потому что значение из списка может измениться в будущем из BE… так что этот слайдер должен быть гибким, используя значение из списка для увеличения или уменьшения галочки