← Назад к вопросам

Какие знаешь виды snapshot?

2.0 Middle🔥 161 комментариев
#State Management#Тестирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Виды Snapshot в Flutter

Snapshot (снимок состояния) — это объект в Flutter, который содержит информацию о состоянии асинхронной операции. Используется с `FutureBuilder` и `StreamBuilder`.

Основные виды snapshots

1. ConnectionState

Определяет состояние асинхронного подключения:

enum ConnectionState {
  none,      // Нет асинхронной операции
  waiting,   // В процессе выполнения (loading)
  active,    // Активное соединение (для Stream)
  done,      // Завершено успешно
}

// Использование
FutureBuilder<String>(
  future: fetchData(),
  builder: (context, snapshot) {
    switch (snapshot.connectionState) {
      case ConnectionState.none:
        return const Text('No data');
      case ConnectionState.waiting:
        return const CircularProgressIndicator();
      case ConnectionState.active:
        return const Text('Active');
      case ConnectionState.done:
        if (snapshot.hasError) {
          return Text('Error: ${snapshot.error}');
        }
        return Text('Data: ${snapshot.data}');
    }
  },
)

2. FutureBuilder Snapshot

Используется для работы с одноразовыми асинхронными операциями (Future).

class FutureBuilderExample extends StatelessWidget {
  Future<String> fetchUserName() async {
    await Future.delayed(const Duration(seconds: 2));
    return 'John Doe';
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<String>(
      future: fetchUserName(),
      builder: (context, snapshot) {
        // snapshot.connectionState — состояние соединения
        // snapshot.data — результат (если есть)
        // snapshot.error — ошибка (если есть)
        // snapshot.hasData — true если есть данные
        // snapshot.hasError — true если есть ошибка
        
        if (snapshot.hasData) {
          return Text('Hello, ${snapshot.data}!');
        } else if (snapshot.hasError) {
          return Text('Error: ${snapshot.error}');
        } else {
          return const CircularProgressIndicator();
        }
      },
    );
  }
}

3. StreamBuilder Snapshot

Используется для работы с потоками данных (Stream).

class StreamBuilderExample extends StatelessWidget {
  Stream<int> countStream() async* {
    for (int i = 0; i < 10; i++) {
      await Future.delayed(const Duration(seconds: 1));
      yield i;
    }
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<int>(
      stream: countStream(),
      initialData: 0,
      builder: (context, snapshot) {
        // snapshot.connectionState — состояние потока
        // snapshot.data — последнее значение из потока
        // snapshot.error — ошибка потока
        // snapshot.hasData — true если есть данные
        // snapshot.hasError — true если есть ошибка
        
        if (snapshot.hasError) {
          return Text('Error: ${snapshot.error}');
        }
        
        if (snapshot.connectionState == ConnectionState.waiting) {
          return const Text('Waiting...');
        }
        
        return Text('Count: ${snapshot.data}');
      },
    );
  }
}

Методы и свойства snapshot

AsyncSnapshot<T> {
  // Состояние соединения
  ConnectionState connectionState;
  
  // Данные результата
  T? data;
  
  // Ошибка (если есть)
  Object? error;
  
  // Stack trace ошибки
  StackTrace? stackTrace;
  
  // Проверочные методы
  bool get hasData => data != null;
  bool get hasError => error != null;
  
  // Получение значения или выброс ошибки
  T get requireData => ...
}

Виды снимков по типам данных

Snapshot с примитивными типами

// Snapshot<String>
FutureBuilder<String>(
  future: Future.value('Hello'),
  builder: (context, AsyncSnapshot<String> snapshot) {
    return Text(snapshot.data ?? 'Loading');
  },
)

// Snapshot<int>
StreamBuilder<int>(
  stream: Stream.periodic(Duration(seconds: 1), (count) => count),
  builder: (context, AsyncSnapshot<int> snapshot) {
    return Text('${snapshot.data ?? 0}');
  },
)

Snapshot со сложными типами

class User {
  final String name;
  final int age;
  User({required this.name, required this.age});
}

// Snapshot<User>
FutureBuilder<User>(
  future: fetchUser(),
  builder: (context, AsyncSnapshot<User> snapshot) {
    if (snapshot.hasData) {
      final user = snapshot.data!;
      return Text('${user.name}, ${user.age}');
    }
    return const SizedBox();
  },
)

// Snapshot<List<User>>
StreamBuilder<List<User>>(
  stream: watchUsers(),
  builder: (context, AsyncSnapshot<List<User>> snapshot) {
    if (snapshot.hasData) {
      return ListView.builder(
        itemCount: snapshot.data!.length,
        itemBuilder: (context, index) {
          final user = snapshot.data![index];
          return ListTile(title: Text(user.name));
        },
      );
    }
    return const CircularProgressIndicator();
  },
)

Снимки с обработкой ошибок

FutureBuilder<String>(
  future: riskyOperation(),
  builder: (context, snapshot) {
    // Вариант 1: последовательная проверка
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const Center(child: CircularProgressIndicator());
    }
    
    if (snapshot.hasError) {
      return Center(
        child: Text('Error: ${snapshot.error}'),
      );
    }
    
    if (snapshot.hasData) {
      return Text('Success: ${snapshot.data}');
    }
    
    return const SizedBox();
  },
)

// Вариант 2: с обработкой ошибок
FutureBuilder<String>(
  future: riskyOperation(),
  builder: (context, snapshot) {
    return snapshot.when(
      data: (data) => Text(data),
      loading: () => const CircularProgressIndicator(),
      error: (error, stackTrace) => Text('Error: $error'),
    );
  },
)

Snapshot в Riverpod (альтернатива)

В современном Flutter часто используют Riverpod вместо FutureBuilder:

final userProvider = FutureProvider<User>((ref) async {
  return fetchUser();
});

class UserWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final userAsync = ref.watch(userProvider);
    
    // AsyncValue с тремя состояниями: data, loading, error
    return userAsync.when(
      data: (user) => Text(user.name),
      loading: () => const CircularProgressIndicator(),
      error: (error, stackTrace) => Text('Error: $error'),
    );
  }
}

Практический пример: загрузка списка

class PostsPage extends StatelessWidget {
  Future<List<Post>> fetchPosts() async {
    final response = await http.get(
      Uri.parse('https://api.example.com/posts'),
    );
    
    if (response.statusCode == 200) {
      final List<dynamic> json = jsonDecode(response.body);
      return json.map((p) => Post.fromJson(p)).toList();
    } else {
      throw Exception('Failed to load posts');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Posts')),
      body: FutureBuilder<List<Post>>(
        future: fetchPosts(),
        builder: (context, snapshot) {
          // Loading state
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(
              child: CircularProgressIndicator(),
            );
          }
          
          // Error state
          if (snapshot.hasError) {
            return Center(
              child: Text(
                'Error: ${snapshot.error}',
                style: const TextStyle(color: Colors.red),
              ),
            );
          }
          
          // Empty data
          if (!snapshot.hasData || snapshot.data!.isEmpty) {
            return const Center(
              child: Text('No posts found'),
            );
          }
          
          // Success state with data
          return ListView.builder(
            itemCount: snapshot.data!.length,
            itemBuilder: (context, index) {
              final post = snapshot.data![index];
              return Card(
                margin: const EdgeInsets.all(8),
                child: ListTile(
                  title: Text(post.title),
                  subtitle: Text(post.body),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

Ключевые точки

ConnectionState.waiting — загрузка данных ✅ snapshot.hasData — данные успешно получены ✅ snapshot.hasError — произошла ошибка ✅ snapshot.data — само значение ✅ snapshot.error — объект ошибки

Когда использовать каждый тип

ТипКогда использовать
FutureBuilderОдноразовые операции (загрузка данных при открытии страницы)
StreamBuilderПостоянные обновления (реал-тайм данные, WebSocket)
RiverpodСложное управление состоянием в крупных приложениях

Snapshot — это фундаментальная концепция в Flutter для работы с асинхронными операциями.