flutter_network_throttler 1.0.0 copy "flutter_network_throttler: ^1.0.0" to clipboard
flutter_network_throttler: ^1.0.0 copied to clipboard

Simulate slow, unreliable, or failing network conditions in Flutter apps to test loading, timeout, and error states.

example/lib/main.dart

import 'dart:async';
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter_network_throttler/flutter_network_throttler.dart';
import 'package:flutter_network_throttler/web_socket.dart';
import 'package:http/http.dart' as http;
import 'package:stream_channel/stream_channel.dart';
import 'package:web_socket_channel/web_socket_channel.dart';

void main() => runApp(const ExampleApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Network Throttler Example',
      theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.blue),
      home: const HomePage(),
    );
  }
}

/// A stand-in backend so the demo needs no real server: it returns a small
/// JSON body for every request after a tiny "server" delay. The throttler wraps
/// this client, so latency/failures/rules are layered on top.
class _DemoBackend extends http.BaseClient {
  @override
  Future<http.StreamedResponse> send(http.BaseRequest request) async {
    await Future<void>.delayed(const Duration(milliseconds: 20));
    final body = utf8.encode('{"ok":true,"path":"${request.url.path}"}');
    return http.StreamedResponse(
      Stream<List<int>>.value(body),
      200,
      contentLength: body.length,
      request: request,
      headers: const {'content-type': 'application/json'},
    );
  }
}

/// An in-memory WebSocket that echoes every frame it is sent, so the demo can
/// show WebSocket frames flowing through the throttler with no real server.
class _EchoChannel extends StreamChannelMixin<dynamic>
    implements WebSocketChannel {
  final StreamController<dynamic> _out = StreamController<dynamic>.broadcast();

  @override
  Stream<dynamic> get stream => _out.stream;

  @override
  WebSocketSink get sink => _EchoSink(_out);

  @override
  Future<void> get ready async {}

  @override
  String? get protocol => null;

  @override
  int? get closeCode => null;

  @override
  String? get closeReason => null;
}

class _EchoSink implements WebSocketSink {
  _EchoSink(this._out);

  final StreamController<dynamic> _out;

  @override
  void add(dynamic data) => _out.add('echo: $data');

  @override
  void addError(Object error, [StackTrace? stackTrace]) {}

  @override
  Future<void> addStream(Stream<dynamic> stream) => stream.forEach(add);

  @override
  Future<void> close([int? closeCode, String? closeReason]) => _out.close();

  @override
  Future<void> get done => _out.done;
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  // The single source of truth the panel edits and the clients read.
  final ThrottleController _controller = ThrottleController(
    profile: ThrottleProfile.initial.copyWith(
      rules: const [
        EndpointRule(
          method: 'GET',
          pattern: '/v1/feed',
          action: DelayAction(Duration(milliseconds: 800)),
        ),
        EndpointRule(
          method: 'POST',
          pattern: '/v1/upload',
          action: FailAction(FailureType.http500),
        ),
        EndpointRule(pattern: '*.cdn.img/*', action: PassThroughAction()),
      ],
    ),
  );

  late final http.Client _client = ThrottleClient(
    _DemoBackend(),
    controller: _controller,
  );

  late final ThrottleWebSocketChannel _socket = ThrottleWebSocketChannel(
    _EchoChannel(),
    controller: _controller,
    url: Uri.parse('wss://demo.example.com/socket'),
  );

  static const _samples = <(String method, String url)>[
    ('GET', 'https://api.example.com/v1/feed?page=2'),
    ('POST', 'https://api.example.com/v1/upload'),
    ('GET', 'https://api.example.com/v1/profile/me'),
    ('GET', 'https://assets.cdn.img/hero.webp'),
  ];

  int _next = 0;
  int _wsSeq = 0;

  @override
  void initState() {
    super.initState();
    // Keep the socket open so echoed frames flow through the throttler.
    _socket.stream.listen((_) {});
  }

  Future<void> _sendHttp() async {
    final (method, url) = _samples[_next++ % _samples.length];
    final uri = Uri.parse(url);
    try {
      if (method == 'POST') {
        await _client.post(uri, body: 'payload');
      } else {
        await _client.get(uri);
      }
    } catch (_) {
      // Simulated failures surface as exceptions — the live log shows them.
    }
  }

  void _sendWs() => _socket.sink.add('ping ${_wsSeq++}');

  @override
  void dispose() {
    _client.close();
    _socket.sink.close();
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // This demo embeds the panel full-screen so you can watch the live log.
    // In a real app you'd instead expose it behind a debug-only trigger, e.g.
    // `NetworkThrottlerButton(controller: _controller)` as a FAB, or a Settings
    // row — both vanish in release builds. See the README for those patterns.
    return Scaffold(
      body: SafeArea(child: NetworkThrottlerPanel(controller: _controller)),
      floatingActionButton: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.end,
        children: [
          FloatingActionButton.extended(
            heroTag: 'ws',
            onPressed: _sendWs,
            icon: const Icon(Icons.bolt_rounded),
            label: const Text('WS frame'),
          ),
          const SizedBox(height: 12),
          FloatingActionButton.extended(
            heroTag: 'http',
            onPressed: _sendHttp,
            icon: const Icon(Icons.send_rounded),
            label: const Text('Send request'),
          ),
        ],
      ),
    );
  }
}
0
likes
160
points
234
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Simulate slow, unreliable, or failing network conditions in Flutter apps to test loading, timeout, and error states.

Repository (GitHub)
View/report issues
Contributing

Topics

#network #testing #http #dio #simulation

License

MIT (license)

Dependencies

dio, flutter, http, meta, stream_channel, web_socket_channel

More

Packages that depend on flutter_network_throttler