sectional_bloc_builder 0.0.2 copy "sectional_bloc_builder: ^0.0.2" to clipboard
sectional_bloc_builder: ^0.0.2 copied to clipboard

Section-aware BlocBuilder for Flutter that rebuilds only tagged UI sections.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:sectional_bloc_builder/sectional_bloc_builder.dart';

void main() {
  runApp(const ExampleApp());
}

class ExampleApp extends StatelessWidget {
  const ExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sectional Bloc Builder Example',
      theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.blue),
      home: BlocProvider(
        create: (_) => ProfileCubit(),
        child: const ProfilePage(),
      ),
    );
  }
}

// 1) Sections
enum ProfileSection { header, details, submitButton }

// 2) State
class ProfileState extends SectionalState<ProfileSection> {
  final String name;
  final String email;

  ProfileState({
    required UiSectionStatus<ProfileSection> uiStatus,
    required this.name,
    required this.email,
  }) : super(uiStatus: uiStatus);

  ProfileState copyWith({
    UiSectionStatus<ProfileSection>? uiStatus,
    String? name,
    String? email,
  }) {
    return ProfileState(
      uiStatus: uiStatus ?? this.uiStatus,
      name: name ?? this.name,
      email: email ?? this.email,
    );
  }
}

// 3) Cubit
class ProfileCubit extends Cubit<ProfileState> {
  ProfileCubit()
      : super(
          ProfileState(
            uiStatus: UiSectionStatus(
              sections: const [], // empty = global
              status: RequestStatus.initial,
            ),
            name: 'Jane',
            email: 'jane@example.com',
          ),
        );

  Future<void> updateEmail(String newEmail) async {
    // Tell only the details section to show loading
    emit(state.copyWith(
      uiStatus: UiSectionStatus(
        sections: const [ProfileSection.details],
        status: RequestStatus.loading,
      ),
    ));

    await Future<void>.delayed(const Duration(milliseconds: 800));

    emit(state.copyWith(
      email: newEmail,
      uiStatus: UiSectionStatus(
        sections: const [ProfileSection.details],
        status: RequestStatus.success,
      ),
    ));
  }
}

class ProfilePage extends StatelessWidget {
  const ProfilePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Profile')),
      body: const Padding(
        padding: EdgeInsets.all(16),
        child: ProfileView(),
      ),
    );
  }
}

class ProfileView extends StatelessWidget {
  const ProfileView({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        // Rebuilds when header is targeted
        SectionalBlocBuilder<ProfileCubit, ProfileState, ProfileSection>(
          sections: const [ProfileSection.header],
          builder: (context, state) {
            return Text('Hello, ${state.name}',
                style: Theme.of(context).textTheme.headlineSmall);
          },
        ),
        const SizedBox(height: 16),

        // Rebuilds when details is targeted
        SectionalBlocBuilder<ProfileCubit, ProfileState, ProfileSection>(
          sections: const [ProfileSection.details],
          builder: (context, state) {
            final loading = state.uiStatus.isLoading;
            return Row(
              children: [
                Text(state.email),
                if (loading)
                  const Padding(
                    padding: EdgeInsets.only(left: 8),
                    child: SizedBox(
                      width: 16,
                      height: 16,
                      child: CircularProgressIndicator(strokeWidth: 2),
                    ),
                  ),
              ],
            );
          },
        ),
        const SizedBox(height: 24),

        // Rebuilds when submitButton is targeted (here we don't drive loading
        // for it, but this shows the pattern)
        SectionalBlocBuilder<ProfileCubit, ProfileState, ProfileSection>(
          sections: const [ProfileSection.submitButton],
          builder: (context, state) {
            final busy = state.uiStatus.isLoading &&
                state.uiStatus.containsSection(ProfileSection.submitButton);
            return ElevatedButton.icon(
              onPressed: busy
                  ? null
                  : () =>
                      context.read<ProfileCubit>().updateEmail('new@example.com'),
              icon: const Icon(Icons.save),
              label: Text(busy ? 'Saving…' : 'Save'),
            );
          },
        ),

        const SizedBox(height: 12),
        Text(
          'Tap Save to update only the details row.\n'
          'Notice header and button don\'t rebuild.',
          style: Theme.of(context).textTheme.bodySmall,
        ),
      ],
    );
  }
}
0
likes
145
points
35
downloads

Publisher

unverified uploader

Weekly Downloads

Section-aware BlocBuilder for Flutter that rebuilds only tagged UI sections.

Repository (GitHub)
View/report issues

Topics

#flutter #bloc #state-management #performance #ui

Documentation

API reference

License

MIT (license)

Dependencies

flutter, flutter_bloc

More

Packages that depend on sectional_bloc_builder