[{"data":1,"prerenderedAt":69},["ShallowReactive",2],{"tutorial-onboarding-animato-in-flutter-con-pageview-e-smoothpageindicator":3,"comments-tutorial-onboarding-animato-in-flutter-con-pageview-e-smoothpageindicator":68},{"id":4,"title":5,"slug":6,"excerpt":7,"intro":8,"cover_image":9,"video_url":10,"difficulty":11,"estimated_minutes":12,"flutter_version":13,"status":14,"published_at":15,"meta_title":16,"meta_description":17,"category":18,"author":22,"steps":24},26,"Onboarding animato in Flutter con PageView e SmoothPageIndicator","onboarding-animato-in-flutter-con-pageview-e-smoothpageindicator","Impara a creare una schermata di onboarding scorrevole con PageView, indicatori animati e navigazione fluida verso la home dell'app.","# Onboarding animato in Flutter\n\nLa schermata di **onboarding** è spesso il primo contatto dell'utente con la tua app: presenta le funzionalità principali attraverso alcune pagine scorrevoli.\n\nIn questo tutorial costruiremo un onboarding completo usando il widget `PageView`, il pacchetto `smooth_page_indicator` per gli indicatori animati e `shared_preferences` per mostrare l'onboarding solo al primo avvio.\n\nAlla fine avrai una base riutilizzabile e professionale da integrare in qualsiasi progetto.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Ftutorials\u002Ff48f930f-9639-4b3c-93cb-149946f4de5c.jpg",null,"beginner",35,"3.x","published","2026-07-02T04:30:48+00:00","Onboarding animato in Flutter con PageView","Guida pratica per creare una schermata di onboarding scorrevole in Flutter con PageView, SmoothPageIndicator e persistenza del primo avvio.",{"id":19,"name":20,"slug":21},1,"Guide","guide",{"id":19,"name":23},"Flutter Bot",[25,32,40,47,54,61],{"id":26,"position":19,"title":27,"body":28,"code_snippet":29,"code_language":30,"expected_result":31,"demo_url":10,"video_url":10},176,"Aggiungere le dipendenze","Aggiungiamo al progetto i pacchetti necessari: `smooth_page_indicator` per gli indicatori animati e `shared_preferences` per ricordare se l'onboarding è già stato completato.\n\nEsegui il comando da terminale oppure aggiungi manualmente le righe al file `pubspec.yaml`.","flutter pub add smooth_page_indicator shared_preferences","bash","Le dipendenze compaiono nel pubspec.yaml e vengono scaricate senza errori.",{"id":33,"position":34,"title":35,"body":36,"code_snippet":37,"code_language":38,"expected_result":39,"demo_url":10,"video_url":10},177,2,"Creare il modello dei contenuti","Definiamo una semplice classe `OnboardingPageData` che rappresenta il contenuto di ogni pagina: titolo, descrizione e icona.\n\nCreiamo poi una lista con le pagine da mostrare.","class OnboardingPageData {\n  final String title;\n  final String description;\n  final IconData icon;\n\n  const OnboardingPageData({\n    required this.title,\n    required this.description,\n    required this.icon,\n  });\n}\n\nconst onboardingPages = \u003COnboardingPageData>[\n  OnboardingPageData(\n    title: 'Benvenuto',\n    description: 'Scopri come la nostra app semplifica la tua giornata.',\n    icon: Icons.waving_hand,\n  ),\n  OnboardingPageData(\n    title: 'Organizza tutto',\n    description: 'Tieni sotto controllo attività e promemoria in un unico posto.',\n    icon: Icons.checklist,\n  ),\n  OnboardingPageData(\n    title: 'Inizia subito',\n    description: 'Registrati in pochi secondi e comincia a usare l\\'app.',\n    icon: Icons.rocket_launch,\n  ),\n];","dart","Hai una lista di tre pagine pronte da visualizzare.",{"id":41,"position":42,"title":43,"body":44,"code_snippet":45,"code_language":38,"expected_result":46,"demo_url":10,"video_url":10},178,3,"Costruire la singola pagina","Creiamo un widget riutilizzabile `OnboardingPage` che riceve un `OnboardingPageData` e ne mostra icona, titolo e descrizione centrati verticalmente.","import 'package:flutter\u002Fmaterial.dart';\n\nclass OnboardingPage extends StatelessWidget {\n  final OnboardingPageData data;\n  const OnboardingPage({super.key, required this.data});\n\n  @override\n  Widget build(BuildContext context) {\n    final theme = Theme.of(context);\n    return Padding(\n      padding: const EdgeInsets.all(32),\n      child: Column(\n        mainAxisAlignment: MainAxisAlignment.center,\n        children: [\n          Icon(data.icon, size: 120, color: theme.colorScheme.primary),\n          const SizedBox(height: 40),\n          Text(\n            data.title,\n            style: theme.textTheme.headlineMedium,\n            textAlign: TextAlign.center,\n          ),\n          const SizedBox(height: 16),\n          Text(\n            data.description,\n            style: theme.textTheme.bodyLarge,\n            textAlign: TextAlign.center,\n          ),\n        ],\n      ),\n    );\n  }\n}","Il widget mostra una pagina ben formattata con icona, titolo e descrizione.",{"id":48,"position":49,"title":50,"body":51,"code_snippet":52,"code_language":38,"expected_result":53,"demo_url":10,"video_url":10},179,4,"Assemblare la schermata con PageView e indicatore","Ora creiamo `OnboardingScreen`, uno `StatefulWidget` che usa un `PageController` e un `PageView.builder` per scorrere le pagine.\n\nAggiungiamo `SmoothPageIndicator` per mostrare la posizione corrente con un'animazione ed espandiamo un pulsante che cambia testo sull'ultima pagina.","import 'package:flutter\u002Fmaterial.dart';\nimport 'package:smooth_page_indicator\u002Fsmooth_page_indicator.dart';\n\nclass OnboardingScreen extends StatefulWidget {\n  final VoidCallback onFinish;\n  const OnboardingScreen({super.key, required this.onFinish});\n\n  @override\n  State\u003COnboardingScreen> createState() => _OnboardingScreenState();\n}\n\nclass _OnboardingScreenState extends State\u003COnboardingScreen> {\n  final _controller = PageController();\n  int _currentPage = 0;\n\n  bool get _isLastPage => _currentPage == onboardingPages.length - 1;\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n\n  void _next() {\n    if (_isLastPage) {\n      widget.onFinish();\n    } else {\n      _controller.nextPage(\n        duration: const Duration(milliseconds: 350),\n        curve: Curves.easeInOut,\n      );\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      body: SafeArea(\n        child: Column(\n          children: [\n            Align(\n              alignment: Alignment.topRight,\n              child: TextButton(\n                onPressed: widget.onFinish,\n                child: const Text('Salta'),\n              ),\n            ),\n            Expanded(\n              child: PageView.builder(\n                controller: _controller,\n                itemCount: onboardingPages.length,\n                onPageChanged: (i) => setState(() => _currentPage = i),\n                itemBuilder: (_, i) => OnboardingPage(data: onboardingPages[i]),\n              ),\n            ),\n            SmoothPageIndicator(\n              controller: _controller,\n              count: onboardingPages.length,\n              effect: const ExpandingDotsEffect(\n                dotHeight: 10,\n                dotWidth: 10,\n              ),\n            ),\n            Padding(\n              padding: const EdgeInsets.all(24),\n              child: SizedBox(\n                width: double.infinity,\n                child: FilledButton(\n                  onPressed: _next,\n                  child: Text(_isLastPage ? 'Inizia' : 'Avanti'),\n                ),\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}","Puoi scorrere le pagine, l'indicatore si anima e il pulsante mostra 'Inizia' sull'ultima pagina.",{"id":55,"position":56,"title":57,"body":58,"code_snippet":59,"code_language":38,"expected_result":60,"demo_url":10,"video_url":10},180,5,"Ricordare se l'onboarding è già stato completato","Usiamo `shared_preferences` per salvare un flag quando l'utente completa l'onboarding, così da non mostrarlo più agli avvii successivi.\n\nCreiamo una piccola classe helper con due metodi.","import 'package:shared_preferences\u002Fshared_preferences.dart';\n\nclass OnboardingStorage {\n  static const _key = 'onboarding_completed';\n\n  Future\u003Cbool> isCompleted() async {\n    final prefs = await SharedPreferences.getInstance();\n    return prefs.getBool(_key) ?? false;\n  }\n\n  Future\u003Cvoid> setCompleted() async {\n    final prefs = await SharedPreferences.getInstance();\n    await prefs.setBool(_key, true);\n  }\n}","Hai un helper che legge e scrive lo stato di completamento dell'onboarding.",{"id":62,"position":63,"title":64,"body":65,"code_snippet":66,"code_language":38,"expected_result":67,"demo_url":10,"video_url":10},181,6,"Decidere quale schermata mostrare all'avvio","Nel `main` leggiamo il flag e decidiamo se mostrare l'`OnboardingScreen` o direttamente la `HomeScreen`.\n\nUsiamo un `FutureBuilder` per attendere la lettura da `shared_preferences` e mostriamo un indicatore di caricamento nel frattempo.","import 'package:flutter\u002Fmaterial.dart';\n\nvoid main() => runApp(const MyApp());\n\nclass MyApp extends StatelessWidget {\n  const MyApp({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      title: 'Onboarding Demo',\n      theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.indigo),\n      home: const AppEntry(),\n    );\n  }\n}\n\nclass AppEntry extends StatefulWidget {\n  const AppEntry({super.key});\n\n  @override\n  State\u003CAppEntry> createState() => _AppEntryState();\n}\n\nclass _AppEntryState extends State\u003CAppEntry> {\n  final _storage = OnboardingStorage();\n  late Future\u003Cbool> _completedFuture;\n\n  @override\n  void initState() {\n    super.initState();\n    _completedFuture = _storage.isCompleted();\n  }\n\n  void _finishOnboarding() async {\n    await _storage.setCompleted();\n    if (!mounted) return;\n    setState(() => _completedFuture = Future.value(true));\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return FutureBuilder\u003Cbool>(\n      future: _completedFuture,\n      builder: (context, snapshot) {\n        if (!snapshot.hasData) {\n          return const Scaffold(\n            body: Center(child: CircularProgressIndicator()),\n          );\n        }\n        if (snapshot.data == true) {\n          return const HomeScreen();\n        }\n        return OnboardingScreen(onFinish: _finishOnboarding);\n      },\n    );\n  }\n}\n\nclass HomeScreen extends StatelessWidget {\n  const HomeScreen({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return const Scaffold(\n      body: Center(child: Text('Benvenuto nella Home!')),\n    );\n  }\n}","Al primo avvio vedi l'onboarding; dopo averlo completato l'app apre direttamente la Home ai riavvii successivi.",[],1785219610160]