Skip to content

Instantly share code, notes, and snippets.

@takenoto
Created December 18, 2022 10:15
Show Gist options
  • Save takenoto/236ec359fe7bf2008a9e81c7cc6f3853 to your computer and use it in GitHub Desktop.
Save takenoto/236ec359fe7bf2008a9e81c7cc6f3853 to your computer and use it in GitHub Desktop.
flutter_web horizontal scroll @ SingleChildScrollView, ListView
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
// Essa primeira mudança é pra permitir o scroll
// ref1: https://docs.flutter.dev/release/breaking-changes/default-scroll-behavior-drag
// ref2: https://stackoverflow.com/questions/69232764/flutter-web-cannot-scroll-with-mouse-down-drag-flutter-2-5
// Basicamente nas versões anteriores do Flutter ele suportava normalmente que a lista fosse arrastada com o mouse.
// Ou seja, é um detalhe específico do Flutter, e diz respeito ao uso de mouse, não a ser web ou mobile.
// Na documentação diz que fizeram a mudança porque se a pessoa quisesse por exemplo permitir a seleção de texto
// em um card em uma lista horizontal, era impossível.
// O que vai ser feito aqui é dar um override global nas listas para que elas possam ser scrolladas clicando e arrastando com
// o mouse. Caso vc queira implementar isso em apenas algumas listas específicas, vai precisar dar um override local.
// tanto no stackoverflow quanto na doc (refs 2 e 1) tem exemplos disso sendo feito.
// É útil quando por exemplo vc só quiser que as listas horizontais sejam scrollaveis com o clique, o resto
// pode ser com a roda do mouse mesmo.
// Copiado da doc [ref1]:
class MyCustomScrollBehavior extends MaterialScrollBehavior {
// Override behavior methods and getters like dragDevices
@override
Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
// etc.
};
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
//--------------------------
// É aqui a mudança que falei
// Você tá dando override no ScrollBehavior DA APLICAÇÃO INTEIRA!!!
scrollBehavior: MyCustomScrollBehavior(),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({
Key? key,
}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// Essa primeira parte é apenas pra fornecer tanto uma ListView.builder quanto um SingleChildScrollView
// pra vc ver a diferença.
// No geral a performance do ListView.builder é melhor por poder renderizar apenas os itens que estão na tela,
// enquanto o SingleChildScrollView vai renderizar tudo e deixar vc scrollar tudo duma vez também.
// Então quanto mais itens menos vale à pena o SingleChildScrollView.
// Index para dizer se vai ser SingleChildScrollView ou ListView
int scrollerChildIndex = 0;
String scrollerChildDescriptionText() => scrollerChildIndex==0?"SingleChildScrollView preserva o tamanho no eixo oposto ao da rolagem":"ListView.builder obriga a preencher todo o espaço do crossAxis (eixo oposto ao da rolagem). Se quiser manter o tamanho normal vc pode colocar dentro de um Center por exemplo.";
final _scrollerChildrenOptions = [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
// Cria 5 FilterTags
for(int i=0; i<16; i++) FilterTag('tag nº $i'),
]
)
),
ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (context, i) => FilterTag('tag nº ${i+1}'),
itemCount:15,
)
];
// Função para alternar entre os dois tipos de scroller que falei
void _swapScroller(){
if(scrollerChildIndex == 0){
scrollerChildIndex=1;
} else if(scrollerChildIndex == 1){
scrollerChildIndex=0;
}
setState((){});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Teste scroll'),
),
body: Column(
children:[
Container(
padding: const EdgeInsets.all(16),
color: Colors.yellow[200],
child:Center(
child: Text(scrollerChildDescriptionText(),
),
),
),
Expanded(
child: _scrollerChildrenOptions[scrollerChildIndex]
),
Container(
padding: const EdgeInsets.all(32),
color: Colors.purple[200],
child: const Center(
child: Text('Seção inferior da tela') ,
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: _swapScroller,
tooltip: 'Increment',
child: const Text('Change'),
),
);
}
}
// Classe FilterTag para simular a sua
class FilterTag extends StatelessWidget{
final String tagText;
const FilterTag(this.tagText);
@override
Widget build(BuildContext context){
return Container(
padding: const EdgeInsets.all(8),
color: Colors.green[300],
child: Text(tagText)
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment